Krótka wersja: poszukiwanie jest znacznie lepsze
Mniej krótka wersja: przeszukiwanie jest na ogół znacznie lepsze, ale bardzo wiele wyszukiwań (spowodowanych na przykład złym projektem zapytań z nieprzyjemnymi skorelowanymi pod-zapytaniami lub ponieważ wykonujesz wiele zapytań w operacji kursora lub innej pętli) może być gorsze niż skanowanie, szczególnie jeśli zapytanie może zwrócić dane z większości wierszy w tabeli, której dotyczy problem.
Pomaga objąć całą rodzinę operacjami wyszukiwania danych, aby w pełni zrozumieć wpływ na wydajność.
Skany tabel: Bez żadnych indeksów, które są istotne dla twojego zapytania, planista jest zmuszony użyć skanu tabeli, co oznacza, że każdy wiersz jest oglądany. Może to spowodować, że każda strona związana z danymi tabeli zostanie odczytana z dysku, co często jest najgorszym przypadkiem. Zauważ, że w przypadku niektórych zapytań użyje skanu tabeli, nawet gdy obecny jest przydatny indeks - dzieje się tak zwykle dlatego, że dane w tabeli są tak małe, że trudniej jest przeglądać indeksy (w takim przypadku można się spodziewać planuj zmiany w miarę wzrostu danych, przy założeniu, że miara selektywności indeksu jest dobra).
Skany indeksu z wyszukiwaniem wierszy: Bez indeksu, który może być bezpośrednio użyty do wyszukiwania, ale indeks zawierający odpowiednie kolumny jest obecny, można zastosować skanowanie indeksu. Na przykład, jeśli masz dużą tabelę z 20 kolumnami z indeksem na kolumnie 1, col2, col3 i wydajesz SELECT col4 FROM exampletable WHERE col2=616
, w tym przypadku skanowanie indeksu w celu zapytania col2
jest lepsze niż skanowanie całej tabeli. Po znalezieniu pasujących wierszy strony danych należy odczytać do pobrania col4 w celu uzyskania danych wyjściowych (lub dalszego łączenia), co jest etapem „wyszukiwania zakładek”, gdy widzisz go w planach zapytań.
Skany indeksu bez przeszukiwania wierszy: Jeśli powyższy przykład byłby,SELECT col1, col2, col3 FROM exampletable WHERE col2=616
dodatkowy wysiłek, aby odczytać strony danych, nie jest potrzebny: po col2=616
znalezieniu pasujących wierszy indeksu znane są wszystkie żądane dane. Dlatego czasami widzisz kolumny, które nigdy nie będą przeszukiwane, ale prawdopodobnie zostaną poproszone o dane wyjściowe, dodane na końcu indeksów - może to zaoszczędzić wyszukiwania wierszy. Podczas dodawania kolumn do indeksu z tego i tylko tego powodu dodaj je z INCLUDE
klauzulą, aby poinformować silnik, że nie musi on optymalizować układu indeksu dla zapytań opartych na tych kolumnach (może to przyspieszyć aktualizacje dokonane w tych kolumnach) . Skany indeksów mogą również wynikać z zapytań bez klauzul filtrujących: SELECT col2 FROM exampletable
skanuje ten przykładowy indeks zamiast stron tabeli.
Szukanie indeksu (z wyszukiwaniem wierszy lub bez) : Podczas wyszukiwania nie uwzględnia się całego indeksu. W przypadku zapytania SELECT * FROM exampletable WHERE c1 BETWEEN 1234 AND 4567
silnik zapytań może znaleźć pierwszy wiersz, który będzie pasował, wykonując wyszukiwanie w indeksie w oparciu o drzewo, c1
a następnie będzie mógł nawigować po indeksie w kolejności, aż dojdzie do końca zakresu (to samo dotyczy zapytania dla c1=1234
jak może istnieć wiele wierszy spełniających warunek nawet dla =
operacji). Oznacza to, że zamiast każdej strony w indeksie (lub tabeli) należy odczytać tylko odpowiednie strony indeksu (plus kilka potrzebnych do pierwszego wyszukiwania).
Indeksy klastrowe: W przypadku indeksu klastrowego dane tabeli są przechowywane w węzłach liści tego indeksu zamiast w osobnej strukturze stosu. Oznacza to, że po znalezieniu wierszy przy użyciu tego indeksu nigdy nie będą potrzebne żadne dodatkowe wyszukiwania wierszy, bez względu na to, jakie kolumny są potrzebne [chyba że masz dane poza stroną, takie jak TEXT
kolumny lub VARCHAR(MAX)
kolumny zawierające długie dane].
Z tego powodu możesz mieć tylko jeden indeks klastrowy [1] , indeks klastrowany jest twoją tabelą zamiast osobnej struktury sterty, więc jeśli użyjesz jednego [2], wybierz go dokładnie, aby uzyskać maksymalny zysk.
Zauważ też, że indeks klastrowany, ponieważ „klucz klastrujący” dla tabeli i jest zawarty w każdym nieklastrowanym indeksie w tabeli, więc szeroki indeks klastrowany nie jest na ogół dobrym pomysłem.
[1] W rzeczywistości można efektywnie mieć wiele indeksów klastrowych, definiując indeksy nieklastrowe, które pokrywają lub uwzględniają każdą kolumnę w tabeli, ale prawdopodobnie marnowanie miejsca ma wpływ na wydajność zapisu, więc jeśli to zrobisz, upewnij się, że naprawdę musisz.
[2] Kiedy mówię „jeśli używasz indeksu klastrowego”, należy pamiętać, że jest on ogólnie zaleca się, aby zrobić jeden na każdym stole. Istnieją wyjątki, jak w przypadku wszystkich praktycznych reguł, tabele, które widzą niewiele innych niż wstawianie zbiorcze i nieuporządkowane odczyty (być może tabele pomostowe dla procesów ETL) są najczęstszym przeciwnym przykładem.
Dodatkowy punkt: Niekompletne skany:
Ważne jest, aby pamiętać, że w zależności od reszty zapytania skanowanie tabeli / indeksu może nie skanować całej tabeli - jeśli logika na to pozwala, plan kwerend może być w stanie spowodować wcześniejsze przerwanie. Najprostszym przykładem tego jest SELECT TOP(1) * FROM HugeTable
- jeśli spojrzysz na plan zapytań, zobaczysz, że ze skanu został zwrócony tylko jeden wiersz, a jeśli obejrzysz statystyki IO ( SET STATISTICS IO ON; SELECT TOP(1) * FROM HugeTable
), zobaczysz, że odczytuje tylko bardzo małą liczbę stron (być może tylko jedną).
To samo może się zdarzyć, jeśli predykat klauzuli WHERE
lub JOIN ... ON
może być uruchomiony jednocześnie ze skanem, który jest źródłem, jeśli jego dane. Planista / moduł uruchamiający zapytania może czasami być bardzo sprytny, jeśli chodzi o przekazywanie predykatów z powrotem w kierunku źródeł danych, aby umożliwić wcześniejsze zakończenie skanowania w ten sposób (a czasami możesz być sprytny w przestawianiu zapytań, aby to zrobić!). Podczas gdy dane przepływają od prawej do lewej zgodnie ze strzałkami na standardowym ekranie planu zapytań, logika działa od lewej do prawej, a każdy krok (od prawej do lewej) niekoniecznie musi zostać zakończony, zanim można będzie rozpocząć następny. W prostym przykładzie powyżej, jeśli spojrzysz na każdy blok w planie zapytań jako agent, SELECT
agent pyta TOP
agenta o wiersz, który z kolei pytaTABLE SCAN
agent dla jednego, następnie SELECT
agent prosi o inny, ale TOP
agent wie, że nie ma potrzeby, nawet nie pyta czytnika tabel, SELECT
agent otrzymuje odpowiedź „nie ma już znaczenia” i wie, że cała praca została wykonana. Wiele operacji blokowania tego typu optymalizacji oczywiście tak często w bardziej skomplikowanych przykładów skan tabeli / index naprawdę nie czytają każdy rząd, ale należy uważać, aby nie skakać do wniosku, że każdy skanowania musi być kosztowna operacja.