2011-05-14 8 views
16

Właśnie zacząłem szukać optymalizacji moich zapytań poprzez indeksy, ponieważ dane SQL rosną szybko i szybko. Sprawdziłem, jak optymalizator przetwarza moje zapytanie za pomocą planu wykonania w SSMS i zauważyłem, że jest używany operator sortowania. Słyszałem, że operator Sortuj wskazuje na zły projekt w zapytaniu, ponieważ sortowanie może zostać przedwcześnie zakończone przez indeks. Więc tutaj jest przykład stół i dane podobny do tego, co robię:Optymalizowanie zapytań SQL przez usunięcie operatora sortowania w planie wykonania

IF OBJECT_ID('dbo.Store') IS NOT NULL DROP TABLE dbo.[Store] 
GO 

CREATE TABLE dbo.[Store] 
(
    [StoreId] int NOT NULL IDENTITY (1, 1), 
    [ParentStoreId] int NULL, 
    [Type] int NULL, 
    [Phone] char(10) NULL, 
    PRIMARY KEY ([StoreId]) 
) 

INSERT INTO dbo.[Store] ([ParentStoreId], [Type], [Phone]) VALUES (10, 0, '2223334444') 
INSERT INTO dbo.[Store] ([ParentStoreId], [Type], [Phone]) VALUES (10, 0, '3334445555') 
INSERT INTO dbo.[Store] ([ParentStoreId], [Type], [Phone]) VALUES (10, 1, '0001112222') 
INSERT INTO dbo.[Store] ([ParentStoreId], [Type], [Phone]) VALUES (10, 1, '1112223333') 
GO 

Oto przykład zapytania:

SELECT [Phone] 
FROM [dbo].[Store] 
WHERE [ParentStoreId] = 10 
AND ([Type] = 0 OR [Type] = 1) 
ORDER BY [Phone] 

utworzyć non indeksu klastrowego, aby pomóc przyspieszyć zapytanie:

CREATE NONCLUSTERED INDEX IX_Store ON dbo.[Store]([ParentStoreId], [Type], [Phone]) 

Aby zbudować indeks IX_Store, zacznę z prostych orzeczników

[ParentStoreId] = 10 
AND ([Type] = 0 OR [Type] = 1) 

Potem dodać kolumnę [Phone] dla ORDER BY oraz pokrycie OUTPUT Należy wybrać

Więc nawet, gdy indeks jest zbudowany, optymalizator nadal używa operatora Sort (a nie rodzaj indeksu), ponieważ [Phone] jest posortowana PO [ParentStoreId] AND [Type]. Jeśli usunąć kolumnę z indeksu [Type] i uruchomić kwerendę:

SELECT [Phone] 
FROM [dbo].[Store] 
WHERE [ParentStoreId] = 10 
--AND ([Type] = 0 OR [Type] = 1) 
ORDER BY [Phone] 

Wtedy oczywiście operator Sortowanie nie jest używany przez optymalizator ponieważ [Phone] jest posortowana według [ParentStoreId].

Pytanie więc, w jaki sposób mogę utworzyć indeks, który obejmie kwerendę (w tym predykat [Type]) i czy optymalizator nie użyje sortowania?

EDIT:

tabela pracuję z ponad 20 milionów wierszy

+0

Naprawdę powinieneś uczynić '[StoreId]' kluczem podstawowym (który również domyślnie klastruje przy okazji), a nie tylko dodać unikalny indeks. – Lucero

+0

Możesz być w stanie obejść ten problem, tworząc * drugi * indeks w kolumnie Telefon. –

+0

@ Lucero, zmodyfikowałem swój post, aby zaznaczyć '[StoreId]' jako klucz podstawowy, chociaż nie sądzę, że rozwiąże to problem sortowania – jodev

Odpowiedz

10

Po pierwsze, należy upewnić się, że porządek jest faktycznie wąskim gardłem wydajności. Czas sortowania będzie zależeć od liczby posortowanych elementów, a liczba sklepów dla określonego magazynu nadrzędnego prawdopodobnie będzie niewielka. (Zakładając, że operator sortowania zostanie zastosowany po zastosowaniu klauzuli where).

Słyszałem, że operator Sortuj wskazuje złą konstrukcję w zapytaniu od sortowania może być wykonany przedwcześnie przez indeks

To nadmierne uogólnienie. Często operator sortowania może być trywialnie przenoszony do indeksu, a jeśli tylko pierwsze pary rekordów wyników są pobierane, może znacznie zmniejszyć koszt zapytania, ponieważ baza danych nie musi już pobierać wszystkich pasujących wierszy (i sortować je all), aby znaleźć pierwsze, ale może odczytać rekordy w kolejności zestawów wyników i zatrzymać po znalezieniu wystarczającej liczby rekordów.

W twoim przypadku wydajesz się, że pobierasz cały zestaw wyników, więc sortowanie jest mało prawdopodobne, aby było znacznie gorzej (chyba że zestaw wyników jest ogromny). Również w twoim przypadku może nie być trywialne zbudowanie przydatnego posortowanego indeksu, ponieważ klauzula where zawiera znak lub.

Teraz, jeśli nadal chcą pozbyć się tego sort-operatora, można spróbować:

SELECT [Phone] 
FROM [dbo].[Store] 
WHERE [ParentStoreId] = 10 
AND [Type] in (0, 1) 
ORDER BY [Phone]  

Alternatywnie, można spróbować następujący wskaźnik:

CREATE NONCLUSTERED INDEX IX_Store ON dbo.[Store]([ParentStoreId], [Phone], [Type]) 

spróbować uzyskiwanie optymalizator zapytań, aby wykonać skanowanie zakresu indeksu tylko na ParentStoreId, a następnie zeskanuj wszystkie pasujące wiersze w indeksie, wyprowadzając je, jeśli pasuje Type. Jednak może to spowodować więcej operacji wejścia/wyjścia dysku, a zatem spowolnić działanie zapytania, a nie przyspieszyć jego działanie.

Edit: W ostateczności można użyć

SELECT [Phone] 
FROM [dbo].[Store] 
WHERE [ParentStoreId] = 10 
AND [Type] = 0 
ORDER BY [Phone] 

UNION ALL 

SELECT [Phone] 
FROM [dbo].[Store] 
WHERE [ParentStoreId] = 10 
AND [Type] = 1 
ORDER BY [Phone] 

z

CREATE NONCLUSTERED INDEX IX_Store ON dbo.[Store]([ParentStoreId], [Type], [Phone]) 

i sortować dwie listy na serwerze aplikacji, gdzie można scalić (jak w seryjnej sortowania) listy wyborów, unikając w ten sposób pełnego sortowania. Ale tak naprawdę jest to mikrooptymalizacja, która, choć przyspiesza sortowanie o rząd wielkości, raczej nie wpłynie na całkowity czas wykonania zapytania, ponieważ spodziewam się, że wąskim gardłem będą sieciowe i dyskowe operacje we/wy, zwłaszcza w związku z tym, że dysk będzie robił wiele losowego dostępu, ponieważ indeks nie jest skupiony.

+0

Tabela, której używam ma więcej niż 20 milionów wierszy. Istnieje około 50 różnych wartości "[ParentStoreId]" i 8 różnych wartości "[Type]". W końcu pozostawiam do sortowania około 200 tysięcy wierszy, co wydaje się spowalniać zapytanie. Twoje informacje są przydatne, ale spróbuję. – jodev

+0

+1 Za sugerowanie unii, aby pominąć operatora sortowania –

Powiązane problemy