Paginated Abfrage Sortierung mit auf verschiedene Spalten mit ROW_NUMBER () OVER () in SQL Server 2005
-
04-07-2019 - |
Frage
Nehmen wir an, ich bin die Northwind-Datenbank, und ich möchte eine Abfrage über eine gespeicherte Prozedur ausführen, die unter anderen Parametern enthält, die folgenden:
-
@Offset
um anzuzeigen, wo die Paginierung beginnt, -
@Limit
die Seitengröße, um anzuzeigen, -
@SortColumn
die Spalte zu Sortierzwecken verwendet, um anzuzeigen, -
@SortDirection
, um aufsteigend oder Nachkommen Sortierung an.
Die Idee ist es, die Paginierung auf der Datenbank zu tun, da die Ergebnismengen Tausende von Zeilen enthalten so Caching ist keine Option (und VIEWSTATE verwendet, wird nicht einmal in Betracht gezogen wie IMO, saugt).
Wie Sie SQL Server 2005 wissen stellt die Funktion ROW_NUMBER die gibt die laufende Nummer einer Zeile innerhalb einer Partition eines Ergebnismenge, auf 1 für die erste Reihe in jeder Partition beginnend .
Wir brauchen Sortierung jede zurück Säule (fünf in diesem Beispiel) auf und dynamische SQL ist keine Option, so haben wir zwei Möglichkeiten: viel IF ... ELSE ...
mit und 10 Abfragen mit, die eine Hölle zu halten, oder eine Abfrage mit wie folgt aus:
WITH PaginatedOrders AS (
SELECT
CASE (@SortColumn + ':' + @SortDirection)
WHEN 'OrderID:A' THEN ROW_NUMBER() OVER (ORDER BY Orders.OrderID ASC)
WHEN 'OrderID:D' THEN ROW_NUMBER() OVER (ORDER BY Orders.OrderID DESC)
WHEN 'CustomerID:A' THEN ROW_NUMBER() OVER (ORDER BY Orders.CustomerID ASC)
WHEN 'CustomerID:D' THEN ROW_NUMBER() OVER (ORDER BY Orders.CustomerID DESC)
WHEN 'EmployeeID:A' THEN ROW_NUMBER() OVER (ORDER BY Orders.EmployeeID ASC)
WHEN 'EmployeeID:D' THEN ROW_NUMBER() OVER (ORDER BY Orders.EmployeeID DESC)
WHEN 'OrderDate:A' THEN ROW_NUMBER() OVER (ORDER BY Orders.OrderDate ASC)
WHEN 'OrderDate:D' THEN ROW_NUMBER() OVER (ORDER BY Orders.OrderDate DESC)
WHEN 'ShippedDate:A' THEN ROW_NUMBER() OVER (ORDER BY Orders.OrderID ASC)
WHEN 'ShippedDate:D' THEN ROW_NUMBER() OVER (ORDER BY Orders.OrderID DESC)
END AS RowNumber,
OrderID, CustomerID, EmployeeID, OrderDate, ShippedDate
FROM Orders
-- WHERE clause goes here
)
SELECT
RowNumber, OrderID, CustomerID, EmployeeID, OrderDate, ShippedDate,
@Offset, @Limit, @SortColumn, @SortDirection
FROM PaginatedOrders
WHERE RowNumber BETWEEN @Offset AND (@Offset + @Limit - 1)
ORDER BY RowNumber
Ich habe versucht, die Abfrage mehrmals mit verschiedenen Argumenten, und seine Leistung ist es eigentlich ganz gut, aber es stillt sieht aus wie es eine andere Art und Weise optimiert werden könnte.
Ist etwas falsch mit dieser Abfrage oder würden Sie es auf diese Weise tun? Schlagen Sie einen anderen Ansatz?
Lösung
Ganz einfach:
SELECT
OrderID, CustomerID, EmployeeID, OrderDate, ShippedDate,
@Offset, @Limit, @SortColumn, @SortDirection
FROM
Orders
WHERE
ROW_NUMBER() OVER
(
ORDER BY
/* same expression as in the ORDER BY of the whole query */
) BETWEEN (@PageNum - 1) * @PageSize + 1 AND @PageNum * @PageSize
/* AND more conditions ... */
ORDER BY
CASE WHEN @SortDirection = 'A' THEN
CASE @SortColumn
WHEN 'OrderID' THEN OrderID
WHEN 'CustomerID' THEN CustomerID
/* more... */
END
END,
CASE WHEN @SortDirection = 'D' THEN
CASE @SortColumn
WHEN 'OrderID' THEN OrderID
WHEN 'CustomerID' THEN CustomerID
/* more... */
END
END DESC
Dieses auf NULL sortieren wird (DESC) wenn ASC Reihenfolge ausgewählt wird, oder umgekehrt.
Lassen Sie die ROW_NUMBER () Funktion Arbeit über die gleiche ORDER BY-Ausdruck.