2009-08-18 15 views
8

Mam tabelę z około 1 miliona rekordów (z systemem SQL Server 2008 Web). Mam procedurę wyszukiwania, która próbuje dopasować kod produktu, a także opis produktu. Jednak w niektórych okolicznościach jest bardzo powolny. Poniżej (cut-dół) Instrukcja SQL:Dlaczego ta instrukcja SQL jest bardzo powolna?

WITH AllProducts AS (
    SELECT  p.*, Row_Number() OVER (ORDER BY ProductId) AS RowNumber 
    FROM  Product AS p 
    WHERE p.IsEnabled=1 AND 
    (
     p.BaseSku = 'KPK-3020QWC-C' -- this on its own is fast 
     OR 
     CONTAINS(p.FreeTextStrings, '"KPK-3020QWC*"') -- and this on its own is fast, but not both 
    ) 
) SELECT * FROM AllProducts   
    WHERE RowNumber BETWEEN 1 AND 20; 

Zauważ, że jeśli po prostu porównać na [p.BaseSku = 'KPK-3020QWC-C'] lub [contains (p.FreeTextStrings, „" KPK-3020QWC * "")] indywidualnie (ale nie jednocześnie) w jednej chwili. A jeśli porównuję je razem, zajmuje to kilka wieków (kilka minut) - i zwraca tylko jeden wiersz.

IsEnabled i BaseSku są indeksowane, a FreeTextStrings jest indeksowane FTS.

Pamiętam, że to było w porządku.

Czy ktoś może rzucić światło na to i zaproponować jakieś rozwiązania?

Plan Wykonanie plik jest dostępny tutaj: http://wiki.webgear.co.nz/GetFile.aspx?File=Temp%5cSearch%20Test.sqlplan.zip

+0

Czy możesz pokazać nam plan wykonania z serwera SQL? –

+0

Te problemy zaczęły występować po uaktualnieniu SQL 2005 do SQL 2008. – Muxa

Odpowiedz

10

or jest notorycznie powolny na SQL Server. To co najmniej drażniące.

spróbuj podzielić go na dwa pytania z union:

WITH AllProducts AS (
    select *, Row_Number() OVER (ORDER BY ProductId) AS RowNumber 
    from (
    SELECT  p.* 
    FROM  Product AS p 
    WHERE p.IsEnabled=1 AND 
     p.BaseSku = 'KPK-3020QWC-C' 
    UNION 
    SELECT  p.* 
    FROM  Product AS p 
    WHERE p.IsEnabled=1 AND 
     CONTAINS(p.FreeTextStrings, '"KPK-3020QWC*"') 
) 
) SELECT * FROM AllProducts   
    WHERE RowNumber BETWEEN 1 AND 20; 
+1

Dokładnie, "lub" najprawdopodobniej powoduje skanowanie tabeli w tabeli produktów, nawet jeśli istnieją indeksy zarówno dla BaseSku, jak i dla FreeTextStrings ... Związek zmieni to indeksować skanowanie metodą wyszukiwania + indeksu ... (zakładając istnienie indeksów obejmujących te dwie kolumny). – KristoferA

+0

Próbowałem tego i rzeczywiście wykazał on wielkie ulepszenia.Spróbuję tej techniki z pełnym poleceniem sql. – Muxa

+0

Tak, to rozwiązało. Jednak myślę, że to dość dziwne, że to samo oświadczenie działało bardzo szybko w SQL 2005 i działa wolno w SQL 2008. Może to być rozwiązane w następnym dodatku Service Pack? – Muxa

1

To wydaje się działać dobrze:

WITH AllProducts AS (
    SELECT  p.*, Row_Number() OVER (ORDER BY ProductId) AS RowNumber 
    FROM  Product AS p 
    WHERE p.IsEnabled=1 AND 
    (
     CONTAINS(p.BaseSku, 'KPK-3020QWC-C') /* instead of p.BaseSku = 'KPK-3020QWC-C' */ 
     OR 
     CONTAINS(p.FreeTextStrings, '"KPK-3020QWC*"') 
    ) 
) SELECT * FROM AllProducts   
    WHERE RowNumber BETWEEN 1 AND 20; 

(miałem już BaseSku FTS-indeksowane)

0

Make sure all necessary indexes are in place. I miał ten sam problem z klauzulą ​​or w jednym z moich zapytań i tworząc INDEKS NIEPRZYCISZONY z kolumnami INCLUDE poprawił wydajność .

Po dalszych badań, to był dołączyć kolumny część indeksu, który naprawdę rozwiązaniu problemu wydajności. Oto co zrobiłem, aby określić problem i jak go rozwiązać:

pomocą planu wykonania, które pomogą Ci tworzyć brakujące indeksy:

bez indeksu zapytanie brał 2+ min, kiedy powinien on mieć uruchomił się w ciągu kilku milisekund. Dlatego porównałem plany wykonania zapytania z klauzulą ​​or w SSMS i bez niej i nie było oczywiste, co muszę zrobić (głównie z powodu braku zrozumienia planów wykonania).

Ale jeśli spojrzysz powyżej planu wykonania w zielony tekst, SSMS może poprosić Cię o utworzenie nieklastrowanego indeksu. Hmm ... warte strzału. Stworzyłem indeks i problem rozwiązany! Możesz kliknąć prawym przyciskiem myszy zapytanie "UTWÓRZ INDEKS" i wybierz "Brakujące szczegóły indeksu ...". Spowoduje to otwarcie nowej karty z pełnym zapytaniem do uruchomienia. Po prostu nadaj mu nazwę.

Powiązane problemy