Die Beispiele in der Frage führen nicht zu den gleichen Ergebnissen (das OFFSET
Beispiel weist einen Fehler von eins auf). Die aktualisierten Formulare unten beheben dieses Problem, entfernen die zusätzliche Sortierung für den ROW_NUMBER
Fall 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_NUMBER
Plan hat geschätzte Kosten von 0.0197935 :
Der OFFSET
Plan hat geschätzte Kosten von 0.0196955 :
Dies entspricht einer Einsparung von 0,000098 geschätzten Kosteneinheiten (obwohl der OFFSET
Plan zusätzliche Operatoren erfordern würde, wenn Sie eine Zeilennummer für jede Zeile zurückgeben möchten). Der OFFSET
Plan 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 30
Literalwerte 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 OFFSET
und 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_NUMBER
Syntax 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
OFFSET
wird 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 OFFSET
oder 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 .