Nowy OFFSET ... FETCH
model wprowadzony wraz z SQL Server 2012 oferuje proste i szybsze stronicowanie. Dlaczego w ogóle istnieją jakiekolwiek różnice, biorąc pod uwagę, że te dwie formy są semantycznie identyczne i bardzo powszechne?
Zakłada się, że optymalizator rozpoznaje oba i optymalizuje je (trywialnie) w pełni.
Oto bardzo prosty przypadek, w którym OFFSET ... FETCH
według szacunków kosztów jest on ~ 2x szybszy.
SELECT * INTO #objects FROM sys.objects
SELECT *
FROM (
SELECT *, ROW_NUMBER() OVER (ORDER BY object_id) r
FROM #objects
) x
WHERE r >= 30 AND r < (30 + 10)
ORDER BY object_id
SELECT *
FROM #objects
ORDER BY object_id
OFFSET 30 ROWS FETCH NEXT 10 ROWS ONLY
Ten przypadek testowy można zmieniać, tworząc CI object_id
lub dodając filtry, ale nie można usunąć wszystkich różnic w planie. OFFSET ... FETCH
jest zawsze szybszy, ponieważ wykonuje mniej pracy w czasie wykonywania.
Odpowiedzi:
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 sortowanieROW_NUMBER
sprawy i wykorzystują zmienne, aby rozwiązanie było bardziej ogólne: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 (sumaOFFSET
iFETCH
) 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ładniOFFSET
, jest kilka powodów:OFFSET
Plan nie gwarantuje się lepiej we wszystkich przypadkachJeden 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
lubROW_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.źródło
Po lekkim skrzypieniu zapytania otrzymuję jednakowy kosztorys (50/50) i równe statystyki IO:
Pozwala to uniknąć dodatkowego sortowania, które pojawia się w Twojej wersji, sortując dalej
r
zamiastobject_id
.źródło
r
zamiast kolumny podstawowej, choćby dlatego, że pasuje do tego, co bym zrobił w nie zagnieżdżonym zapytaniu i porządkowania według wyrażenia - używałbym aliasu przypisanego do wyrażenia zamiast powtarzania wyrażenia.Zmodyfikowali optymalizator zapytań, aby dodać tę funkcję. Oznacza to, że wdrożyli mechanizmy specjalnie do obsługi polecenia offset ... fetch. Innymi słowy, dla najpopularniejszego zapytania SQL Server musi wykonać znacznie więcej pracy. Zatem różnica w planach zapytań.
źródło