Dlaczego SQL Server miałby ignorować indeks?

16

Mam tabelę CustPassMasterz 16 kolumnami, z których jedna jest CustNum varchar(8), i utworzyłem indeks IX_dbo_CustPassMaster_CustNum. Kiedy uruchamiam moje SELECToświadczenie:

SELECT * FROM dbo.CustPassMaster WHERE CustNum = '12345678'

Całkowicie ignoruje indeks. To mnie myli, ponieważ mam inną tabelę CustDataMasterze znacznie większą liczbą kolumn (55), z których jedna to CustNum varchar(8). Utworzyłem indeks dla tej kolumny ( IX_dbo_CustDataMaster_CustNum) w tej tabeli i używam praktycznie tego samego zapytania:

SELECT * FROM dbo.CustDataMaster WHERE CustNum = '12345678'

I korzysta z indeksu, który utworzyłem.

Czy kryje się za tym jakieś uzasadnienie? Dlaczego miałby korzystać z indeksu z CustDataMaster, ale nie z tego CustPassMaster? Czy wynika to z niskiej liczby kolumn?

Pierwsze zapytanie zwraca 66 wierszy. Po drugie zwracany jest 1 wiersz.

Ponadto dodatkowa uwaga: CustPassMasterma 4991 rekordów i CustDataMaster5376 rekordów. Czy może to być powodem ignorowania indeksu? CustPassMasterma również zduplikowane rekordy, które również mają te same CustNumwartości. Czy to kolejny czynnik?

Opieram to twierdzenie na faktycznych wynikach planu wykonania obu zapytań.

Oto DDL dla CustPassMaster(tego z nieużywanym indeksem):

CREATE TABLE dbo.CustPassMaster(
    [CustNum] [varchar](8) NOT NULL,
    [Username] [char](15) NOT NULL,
    [Password] [char](15) NOT NULL,
    /* more columns here */
    [VBTerminator] [varchar](1) NOT NULL
) ON [PRIMARY]

CREATE NONCLUSTERED INDEX [IX_dbo_CustPassMaster_CustNum] ON dbo.CustPassMaster
(
    [CustNum] ASC
) WITH (PAD_INDEX = OFF
    , STATISTICS_NORECOMPUTE = OFF
    , SORT_IN_TEMPDB = OFF
    , DROP_EXISTING = OFF
    , ONLINE = OFF
    , ALLOW_ROW_LOCKS = ON
    , ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

I DDL dla CustDataMaster(pominąłem wiele niepotrzebnych pól):

CREATE TABLE dbo.CustDataMaster(
    [CustNum] [varchar](8) NOT NULL,
    /* more columns here */
    [VBTerminator] [varchar](1) NOT NULL
) ON [PRIMARY]

CREATE NONCLUSTERED INDEX [IX_dbo_CustDataMaster_CustNum] ON dbo.CustDataMaster
(
    [CustNum] ASC
)WITH (PAD_INDEX = OFF
    , STATISTICS_NORECOMPUTE = OFF
    , SORT_IN_TEMPDB = OFF
    , DROP_EXISTING = OFF
    , ONLINE = OFF
    , ALLOW_ROW_LOCKS = ON
    , ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

Nie mam indeksu klastrowego w żadnej z tych tabel, tylko jeden indeks nieklastrowany.

Zignoruj ​​fakt, że typy danych nie są całkowicie zgodne z typem przechowywanych danych. Te pola stanowią kopię zapasową bazy danych IBM AS / 400 DB2 i są to zgodne typy danych dla niej. (Muszę być w stanie wykonać zapytanie do tej zapasowej bazy danych dokładnie tymi samymi zapytaniami i uzyskać dokładnie takie same wyniki).

Te dane są wykorzystywane tylko do SELECTwyciągów. Nie wykonuję na nim żadnych instrukcji INSERT/ UPDATE/ DELETE, z wyjątkiem sytuacji, gdy aplikacja do kopiowania kopiuje dane z AS / 400.

Der Kommissar
źródło
Warto przeczytać ten artykuł na temat punktu krytycznego od NonClustered do Clustered. sqlskills.com/blogs/kimberly/the-tipping-point-query-answers
Mark Sinkinson
3
To jest różnica. Jeśli pierwsze zapytanie użyło Twojego indeksu, musiałoby wykonać 65 wyszukiwań. To jest drogie. Drugie zapytanie musi wykonać tylko jedno.
Aaron Bertrand

Odpowiedzi:

18

Zazwyczaj indeksy będą używane przez SQL Server, jeśli uzna, że ​​bardziej użyteczne jest użycie indeksu niż bezpośrednie użycie tabeli bazowej.

Wydaje się prawdopodobne, że optymalizator oparty na kosztach uważa, że ​​korzystanie z danego indeksu byłoby droższe. Możesz zobaczyć, że używa indeksu, jeśli zamiast tego SELECT *po prostu SELECT T1Col1.

Gdy mówisz SELECT *programowi SQL Server, aby zwrócił wszystkie kolumny w tabeli. Aby zwrócić te kolumny, SQL Server musi czytać strony dla wierszy, które pasują do WHEREkryteriów instrukcji z samej tabeli (indeks klastrowy lub sterta). SQL Server prawdopodobnie myśli, że ilość odczytów wymaganych do pobrania pozostałych kolumn z tabeli oznacza, że ​​równie dobrze może bezpośrednio zeskanować tabelę. Przydałoby się zobaczyć rzeczywiste zapytanie i faktyczny plan wykonania użyty przez zapytanie.

Max Vernon
źródło
3
Czy bardziej oczywiste i optymalne byłoby dla mnie ograniczenie wybranych kolumn i włączenie ich do INCLUDEklauzuli indeksu?
Der Kommissar
1
To może bardzo wiele zmienić. Dodanie wszystkich kolumn zwróconych przez zapytanie do INCLUDEklauzuli prawdopodobnie spowoduje, że SQL Server użyje indeksu. Powiedziawszy to, co próbujesz zoptymalizować? Wydaje mi się, że jeśli twoja tabela ma średni rozmiar wiersza 100 bajtów, to 5000 wierszy to tylko około 500 KB danych i może nie być warte spędzania czasu.
Max Vernon,
1
Średni rozmiar wiersza wynosi 0,30 KB dla Table1i 0,53 KB dla Table2. Wszystkie te dane są importowane z AS / 400 (IBM System i) i na żadnym nie ma żadnych PK. Dzisiaj ręcznie utworzyłem wszystkie indeksy po tym, jak ludzie wspominali, że czasami aplikacja działa dość wolno.
Der Kommissar
10

Aby używać indeksu, ponieważ robisz to select *, SQL Server musi najpierw odczytać każdy z wierszy indeksu, które pasują do wartości, którą masz w klauzuli where. Na tej podstawie otrzyma wartości indeksu klastrowanego dla każdego wiersza, a następnie będzie musiał szukać każdego z nich oddzielnie od indeksu klastrowanego (= wyszukiwanie klucza). Ponieważ powiedziałeś, że wartości nie są unikalne, SQL Server używa statystyk do oszacowania, ile razy musi wykonać to wyszukiwanie klucza.

Najprawdopodobniej koszt oszacowania skanowania indeksu nieklastrowanego + wyszukiwania klucza przekracza szacunkowy koszt skanowania indeksu klastrowanego i dlatego indeks jest ignorowany.

Możesz spróbować użyć, set statistics io ona następnie użyć podpowiedzi do indeksu, aby sprawdzić, czy koszt we / wy jest rzeczywiście mniejszy podczas korzystania z indeksu, czy nie. Jeśli różnica jest duża, możesz spojrzeć na statystyki, jeśli są nieaktualne.

Ponadto, jeśli SQL faktycznie używa zmiennych, a nie dokładnych wartości, może to być również spowodowane wąchaniem parametrów (= poprzednia wartość użyta do utworzenia planu miała wiele wierszy w tabeli).

James Z
źródło
1

To może być powód. Optymalizatory są oparte na kosztach i decydują, którą ścieżkę wybrać na podstawie „kosztu”, jaki ma każda ścieżka wykonania. „Największy” koszt to przeniesienie danych z dysku do pamięci. Jeśli optymalizator obliczy, że odczyt indeksu i danych zajmuje więcej czasu, może zdecydować o pominięciu indeksu. Im większe rzędy, tym więcej bloków dyskowych zajmują.

Marco
źródło