Przykłady w pytaniu nie dają takich samych wyników (w OFFSETprzykładzie występuje błąd „jeden po drugim”). Poniższe zaktualizowane formularze rozwiązują ten problem, usuwają dodatkowe sortowanie ROW_NUMBERsprawy i wykorzystują zmienne, aby rozwiązanie było bardziej ogólne:
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;
ROW_NUMBERPlan ma szacunkowy koszt 0.0197935 :

OFFSETPlan ma szacunkowy koszt 0.0196955 :

Jest to oszczędność 0,000098 szacowanych jednostek kosztów (chociaż OFFSETplan wymagałby dodatkowych operatorów, jeśli chcesz zwrócić numer wiersza dla każdego wiersza). OFFSETPlan nadal będą nieco tańsze, ogólnie rzecz biorąc, ale pamiętam, że szacowane koszty są dokładnie tym - nadal wymagane jest prawdziwe testy. Większość kosztów w obu planach to koszt pełnego rodzaju zestawu danych wejściowych, więc pomocne indeksy byłyby korzystne dla obu rozwiązań.
Tam, gdzie stosowane są stałe wartości literalne (np. OFFSET 30W oryginalnym przykładzie), optymalizator może użyć sortowania TopN zamiast pełnego sortowania, po którym następuje Top. Gdy wiersze potrzebne w sortowaniu TopN są stałym literałem, a <= 100 (suma OFFSETi FETCH) silnik wykonawczy może użyć innego algorytmu sortowania, który może działać szybciej niż uogólnione sortowanie TopN. Wszystkie trzy przypadki mają ogólnie różne charakterystyki wydajności.
Powód, dla którego optymalizator nie przekształca automatycznie ROW_NUMBERwzorca składni OFFSET, jest kilka powodów:
- Niemal niemożliwe jest napisanie transformacji, która pasowałaby do wszystkich istniejących zastosowań
- Automatyczne przekształcanie niektórych zapytań stronicujących i brak innych może być mylące
OFFSETPlan nie gwarantuje się lepiej we wszystkich przypadkach
Jeden przykład dla trzeciego punktu powyżej występuje, gdy zestaw stronicowania jest dość szeroki. Znacznie bardziej wydajne może być wyszukiwanie potrzebnych kluczy za pomocą indeksu nieklastrowanego i ręczne wyszukiwanie indeksu klastrowanego w porównaniu ze skanowaniem indeksu za pomocą OFFSETlub ROW_NUMBER. Są dodatkowe kwestie do rozważenia, jeśli aplikacja stronicująca musi wiedzieć, ile jest w sumie wierszy lub stron. Nie ma innego dobra dyskusja o zaletach „klucza szukać” i „przesunięcie” metody tutaj .
Ogólnie rzecz biorąc, prawdopodobnie lepiej jest, aby ludzie podjęli świadomą decyzję o zmianie zapytań stronicowania, aby w OFFSETrazie potrzeby użyć ich po dokładnych testach.