Die Beispiele in der Frage führen nicht zu den gleichen Ergebnissen (das OFFSETBeispiel weist einen Fehler von eins auf). Die aktualisierten Formulare unten beheben dieses Problem, entfernen die zusätzliche Sortierung für den ROW_NUMBERFall und verwenden Variablen, um die Lösung allgemeiner zu gestalten:
DECLARE
@PageSize bigint = 10,
@PageNumber integer = 3;
WITH Numbered AS
(
SELECT TOP ((@PageNumber + 1) * @PageSize)
o.*,
rn = ROW_NUMBER() OVER (
ORDER BY o.[object_id])
FROM #objects AS o
ORDER BY
o.[object_id]
)
SELECT
x.name,
x.[object_id],
x.principal_id,
x.[schema_id],
x.parent_object_id,
x.[type],
x.type_desc,
x.create_date,
x.modify_date,
x.is_ms_shipped,
x.is_published,
x.is_schema_published
FROM Numbered AS x
WHERE
x.rn >= @PageNumber * @PageSize
AND x.rn < ((@PageNumber + 1) * @PageSize)
ORDER BY
x.[object_id];
SELECT
o.name,
o.[object_id],
o.principal_id,
o.[schema_id],
o.parent_object_id,
o.[type],
o.type_desc,
o.create_date,
o.modify_date,
o.is_ms_shipped,
o.is_published,
o.is_schema_published
FROM #objects AS o
ORDER BY
o.[object_id]
OFFSET @PageNumber * @PageSize - 1 ROWS
FETCH NEXT @PageSize ROWS ONLY;
Der ROW_NUMBERPlan hat geschätzte Kosten von 0.0197935 :

Der OFFSETPlan hat geschätzte Kosten von 0.0196955 :

Dies entspricht einer Einsparung von 0,000098 geschätzten Kosteneinheiten (obwohl der OFFSETPlan zusätzliche Operatoren erfordern würde, wenn Sie eine Zeilennummer für jede Zeile zurückgeben möchten). Der OFFSETPlan wird im Allgemeinen immer noch etwas billiger sein, aber denken Sie daran, dass die geschätzten Kosten genau so hoch sind - echte Tests sind immer noch erforderlich. Der Großteil der Kosten in beiden Plänen sind die Kosten für die gesamte Art der Eingabemenge, sodass hilfreiche Indizes beiden Lösungen zugute kommen würden.
Wenn konstante OFFSET 30Literalwerte verwendet werden (z. B. im ursprünglichen Beispiel), kann der Optimierer eine TopN-Sortierung anstelle einer vollständigen Sortierung gefolgt von einer Top-Sortierung verwenden. Wenn die für die TopN-Sortierung benötigten Zeilen ein konstantes Literal sind und <= 100 (die Summe von OFFSETund FETCH), kann die Ausführungsengine einen anderen Sortieralgorithmus verwenden, der eine schnellere Leistung als die verallgemeinerte TopN-Sortierung liefert. Alle drei Fälle weisen insgesamt unterschiedliche Leistungsmerkmale auf.
Wie, warum sich der Optimierer nicht automatisch die Transformation ROW_NUMBERSyntax Muster zu verwenden OFFSET, gibt es eine Reihe von Gründen:
- Es ist fast unmöglich, eine Transformation zu schreiben, die allen vorhandenen Verwendungen entspricht
- Es kann verwirrend sein, wenn einige Paging-Abfragen automatisch umgewandelt werden und andere nicht
- Es
OFFSETwird nicht garantiert, dass der Plan in allen Fällen besser ist
Ein Beispiel für den dritten Punkt oben ist, dass der Paging-Satz ziemlich breit ist. Es kann wesentlich effizienter sein , die benötigten Schlüssel mit einem nicht gruppierten Index zu suchen und manuell nach dem gruppierten Index zu suchen , als den Index mit OFFSEToder zu durchsuchen ROW_NUMBER. Es sind weitere Probleme zu berücksichtigen, wenn die Paging-Anwendung wissen muss, wie viele Zeilen oder Seiten insgesamt vorhanden sind. Es ist auch eine gute Diskussion über die Vorzüge des ‚Schlüssel suchen‘ und ‚Offset‘ Methoden hier .
Insgesamt ist es wahrscheinlich besser OFFSET, wenn die Benutzer eine fundierte Entscheidung treffen, ihre Paging-Abfragen gegebenenfalls nach gründlichen Tests zu ändern .