Przykłady w pytaniu nie dają takich samych wyników (w OFFSET
przykładzie występuje błąd „jeden po drugim”). Poniższe zaktualizowane formularze rozwiązują ten problem, usuwają dodatkowe sortowanie ROW_NUMBER
sprawy 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_NUMBER
Plan ma szacunkowy koszt 0.0197935 :
OFFSET
Plan ma szacunkowy koszt 0.0196955 :
Jest to oszczędność 0,000098 szacowanych jednostek kosztów (chociaż OFFSET
plan wymagałby dodatkowych operatorów, jeśli chcesz zwrócić numer wiersza dla każdego wiersza). OFFSET
Plan 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 30
W 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 OFFSET
i 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_NUMBER
wzorca 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
OFFSET
Plan 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ą OFFSET
lub 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 OFFSET
razie potrzeby użyć ich po dokładnych testach.