Mam tabelę CustPassMaster
z 16 kolumnami, z których jedna jest CustNum varchar(8)
, i utworzyłem indeks IX_dbo_CustPassMaster_CustNum
. Kiedy uruchamiam moje SELECT
oświadczenie:
SELECT * FROM dbo.CustPassMaster WHERE CustNum = '12345678'
Całkowicie ignoruje indeks. To mnie myli, ponieważ mam inną tabelę CustDataMaster
ze 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: CustPassMaster
ma 4991 rekordów i CustDataMaster
5376 rekordów. Czy może to być powodem ignorowania indeksu? CustPassMaster
ma również zduplikowane rekordy, które również mają te same CustNum
wartoś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 SELECT
wyciągów. Nie wykonuję na nim żadnych instrukcji INSERT
/ UPDATE
/ DELETE
, z wyjątkiem sytuacji, gdy aplikacja do kopiowania kopiuje dane z AS / 400.
źródło
Odpowiedzi:
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 prostuSELECT 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ą doWHERE
kryterió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.źródło
INCLUDE
klauzuli indeksu?INCLUDE
klauzuli 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.Table1
i 0,53 KB dlaTable2
. 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.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 on
a 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).
źródło
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ą.
źródło