Einige ASP.NET-Controls besitzen die Möglichkeit zum seitenweisen Anzeigen der übergebenen Daten (Paging), was auf den ersten Blick sehr sinnvoll ist, um dem Benutzer nicht endlos scrollen lassen zu müssen. Ein Klick auf "nächste Seite" und die nächsten 20 Treffer werden angezeigt. Auf den ersten Blick eine schicke Sache, auf den zweiten aber nicht besonders effizient, denn die gesamten Daten, werden von der ASP.NET.Applikation im Speicher gehalten!
Die bessere Alternative ist ja wohl, sich immer nur die Datenmenge vom Datenbank-Server zu holen, die in dem Moment gebraucht wird, also SQL-Server-Paging zu implementieren.
Bis einschließlich SQL-Server 2000 war ein effizientes serverseitiges Paging mittels T-SQL nicht immer ein Vergnügen. Entweder man pumpte die Daten über eine Stored-Procedure in temporäre Tabellen und ließ sich den Teil der Daten zurückgeben den man gerade brauchte oder man erzeugte dynamisches SQL, was auf die Dauer nur sehr schlecht wartbar war.
Mit der Veröffentlichung der 2005'er-Version des SQL-Servers hielt ein Feature Einzug, daß sich wunderbar für Paging nutzen lässt: ROW_NUMBER.
Kurz etwas zum Feature selbst, auf das Entwickler lange gewartet haben:
Mittels ROW_NUMBER ist man in der Lage dem Resultset einer beliebigen T-SQL-Abfrage Zeilennummer zu verleihen. Zum Beispiel:
SELECT
ROW_NUMBER() OVER(ORDER BY ModifiedDate DESC) AS 'RowNr',
FirstName,
LastName
FROM AdventureWorks.Person.Contact
WHERE EmailPromotion = 2
RowNr FirstName LastName
------ ------------- --------------
1 Humberto Acevedo
2 Gustavo Achong
3 Kate Xie
4 Bianca Hu
5 Christina Morris
6 Rebecca Nicholls
...
Über das Argument partition_by_clause kann man sogar Zeilennummern innerhalb einer Gruppe von Datensätzen erzeugen, aber das nur am Rande.
Mit dieser Zeilennummer hat man nun das Werkzeug in der Hand, um über eine Stored-Procedure sehr effektiv ein server-seitiges Paging zu implementieren:
CREATE PROCEDURE procContactsForPromotion
@Page INT,
@PageSize INT
AS
BEGIN
SET NOCOUNT ON;
WITH rsContacts AS
(
SELECT
ROW_NUMBER() OVER(ORDER BY ModifiedDate DESC) AS 'RowNr',
FirstName,
LastName
FROM AdventureWorks.Person.Contact
WHERE EmailPromotion = 2
)
SELECT *
FROM rsContacts
WHERE RowNr BETWEEN (@Page - 1) * @PageSize + 1
AND @Page * @PageSize
END
Einfacher und performanter gehts nicht. Folgender Aufruf gibt für dieses Beispiel die Datensätze 31 bis 40 aus:
EXEC procContactsForPromotion @Page = 4, @PageSize = 10