2009-11-03 16 views
6

Mam problemy ze zrozumieniem zachowania planów szacunkowych zapytań dla mojego wyciągu w programie SQL Server w przypadku zmiany ze sparametryzowanego zapytania na sparametryzowane zapytanie.Różnice w planach zapytań SQL Server

mam zapytania:

DECLARE @p0 UniqueIdentifier = '1fc66e37-6eaf-4032-b374-e7b60fbd25ea' 
SELECT [t5].[value2] AS [Date], [t5].[value] AS [New] 
FROM (
    SELECT COUNT(*) AS [value], [t4].[value] AS [value2] 
    FROM (
     SELECT CONVERT(DATE, [t3].[ServerTime]) AS [value] 
     FROM (
      SELECT [t0].[CookieID] 
      FROM [dbo].[Usage] AS [t0] 
      WHERE ([t0].[CookieID] IS NOT NULL) AND ([t0].[ProductID] = @p0) 
      GROUP BY [t0].[CookieID] 
      ) AS [t1] 
     OUTER APPLY (
      SELECT TOP (1) [t2].[ServerTime] 
      FROM [dbo].[Usage] AS [t2] 
      WHERE ((([t1].[CookieID] IS NULL) AND ([t2].[CookieID] IS NULL)) 
      OR (([t1].[CookieID] IS NOT NULL) AND ([t2].[CookieID] IS NOT NULL) 
      AND ([t1].[CookieID] = [t2].[CookieID]))) 
      AND ([t2].[CookieID] IS NOT NULL)   
      AND ([t2].[ProductID] = @p0) 
      ORDER BY [t2].[ServerTime] 
      ) AS [t3] 
     ) AS [t4] 
    GROUP BY [t4].[value] 
    ) AS [t5] 
ORDER BY [t5].[value2] 

Zapytanie wytworzony przez ekspresję linq2sql i ekstrahowano z LINQPad. Tworzy to przyjemny plan kwerend (o ile mogę powiedzieć) i wykonuje się w około 10 sekund w bazie danych. Jeśli jednak zastąpię dwa zastosowania parametrów dokładną wartością, czyli zastąpię dwie części "= @ p0" za pomocą "=" 1fc66e37-6eaf-4032-b374-e7b60fbd25ea "" Otrzymuję inny szacowany plan zapytania i zapytanie działa teraz znacznie dłużej (ponad 60 sekund, nie widziałem tego).

Dlaczego wykonanie pozornie niewinnego zamiennika zapewnia znacznie mniej wydajny plan kwerend i wykonanie? Usunąłem pamięć podręczną procedur z "DBCC FreeProcCache", aby upewnić się, że nie buforowałem złego planu, ale zachowanie pozostaje.

Moim prawdziwym problemem jest to, że mogę żyć z 10-sekundowym czasem wykonania (przynajmniej na dobry moment), ale nie mogę żyć z czasem realizacji 60+ sekund. Moja kwerenda będzie (jak sugerował wyżej) przez produkowane przez linq2sql więc jest on wykonywany na bazie danych jako

exec sp_executesql N' 
     ... 
     WHERE ([t0].[CookieID] IS NOT NULL) AND ([t0].[ProductID] = @p0) 
     ... 
     AND ([t2].[ProductID] = @p0) 
     ... 
     ',N'@p0 uniqueidentifier',@p0='1FC66E37-6EAF-4032-B374-E7B60FBD25EA' 

która produkuje ten sam czas wykonania słabe (co moim zdaniem jest podwójnie dziwne, ponieważ wydaje się być za pomocą sparametryzowanych kwerend.

ja nie szukam porad, na których indeksy stworzenia lub podobnego, po prostu próbuję zrozumieć, dlaczego plan kwerend i wykonanie są tak niepodobne na trzech pozornie podobnych zapytań

EDIT:. Przesłałem plany wykonania dla sparametryzowanych i zapytania parametryzowane a także plan wykonania dla zapytania parametrycznego (jako sugerowane przez Heinz) z innym GUID here

Nadzieję, że pomaga mi pomóc :)

+1

Czy możesz opublikować plany zapytań, które otrzymujesz? Po prostu uruchom 'SET SHOWPLAN_TEXT ON GO SELECT ...' – Quassnoi

+0

Zrobione ... Dodano łącze do planu wykonania ... –

Odpowiedz

2

ja nie szukam porad, na których indeksy stworzenia lub podobnego, po prostu próbuję zrozumieć, dlaczego plan kwerend i wykonanie są tak niepodobne na trzech pozornie podobnych zapytań.

Wydaje się mieć dwa indeksy:

IX_NonCluster_Config (ProductID, ServerTime) 
IX_NonCluster_ProductID_CookieID_With_ServerTime (ProductID, CookieID) INCLUDE (ServerTime) 

Pierwszy indeks nie pokrywają CookieID ale jest zamawiany na ServerTime i stąd jest bardziej efektywne dla mniej selektywnych ProductID „s (czyli tych, które masz wielu)

Drugi indeks obejmuje wszystkie kolumny, ale nie jest uporządkowany, a zatem jest bardziej wydajny dla więcej selektywny ProductID (te, które masz niewiele).

Średnio, ty ProductID liczność jest tak, że SQL Server oczekuje druga metoda będzie skuteczna, co jest, co używa podczas używania sparametryzowanych kwerend lub wyraźnie dostarczenie selektywnych GUID „s.

Jednak oryginalna wersja GUID jest uważana za mniej selektywną, dlatego używana jest pierwsza metoda.

Niestety, pierwsza metoda wymaga dodatkowego filtrowania na CookieID, przez co jest mniej skuteczna.

+0

Ahh ... może to również wyjaśnia, dlaczego otrzymuję "szybkie" wykonanie, jeśli usunę zbędne części klauzuli where (części OR, które sprawdzają, czy IS NULL jest zbędne), ponieważ nie jest wymagane dodatkowe filtrowanie na CookieID. Kwerenda generowana jest przez Linq2SQL, którego naprawdę nie mogę zmodyfikować, ale sprawdzę, czy mogę utworzyć kolumnę Not-Null, która wydaje się usuwać klauzule additonal i generować szybkie zapytanie. –

1

Domyślam się, że jeśli wziąć non trasę paramaterized , twój guid musi zostać przekonwertowany z varchar na UniqueIdentifier, co może spowodować, że indeks nie zostanie użyty, podczas gdy będzie używany przy paramatarized route.

Widziałem to dzieje się przy użyciu kwerendy, które mają smalldatetime w klauzuli where z kolumna, która używa datetime.

3

Jeśli podasz konkretną wartość, SQL Server może użyć statystyk z tego pola, aby podjąć "lepszą" decyzję dotyczącą planu kwerend. Niestety (jak ostatnio doświadczyłem), jeśli informacje zawarte w statystykach wprowadzają w błąd, czasami SQL Server po prostu robi złe wybory.

Jeśli chcesz zagłębić się w ten problem, polecam sprawdzić, co się stanie, jeśli użyjesz innych identyfikatorów GUID: jeśli używa innego planu zapytań dla różnych konkretnych identyfikatorów GUID, oznacza to, że dane statystyczne są używane. W takim przypadku możesz przyjrzeć się sp_updatestats i powiązanym poleceniom.

EDIT: Zapraszamy do obejrzenia DBCC SHOW_STATISTICS: Opcja „wolno” i „szybko” GUID są chyba w różnych wiadrach w histogramie. Mam had a similar problem, który rozwiązałem przez dodanie do SQL INDEX table hint, który "prowadzi" SQL Server do znalezienia "właściwego" planu zapytania.Zasadniczo przyjrzałem się tym, które wskaźniki są używane podczas "szybkiego" zapytania i które są zakodowane w SQL. To dalekie od optymalnego lub eleganckiego rozwiązania, ale jeszcze nie znalazłem lepszego ...

+0

Po prostu próbowałem uruchomić powolne, nieprogramowalne z innym identyfikatorem GUID i wygenerował ładny plan zapytania i wykonano zgodnie z oczekiwaniami. Czy mógłbyś ewentualnie rozwinąć to, czego potrzebuję szukać w odniesieniu do statystyk? Czy jest to konkretny indeks, który musi zostać odbudowany lub podobny? –

+0

Edytowałem mój wpis, aby dodać więcej szczegółów. – Heinzi

+0

Wprawdzie moje pierwsze spojrzenie na DBBC SHOW_STATISTICS, ale wydaje mi się, że rozszyfrowuję, że identyfikatory GUID są w oddzielnych segmentach ("wolny" z RANGE_ROWS równy 316, a "szybki" z RANGE_ROWS równy 0 (?)). Niestety używam Linq2SQL, więc nie mam prawdziwej ścieżki do ustawiania wskazówek dotyczących zapytań. Czy mógłbym jakoś przeliczyć statystyki? –

0

Trudno powiedzieć, nie patrząc na plany wykonania, ale gdybym miał zgadywać z jakiegoś powodu Powiedziałbym, że jest to kombinacja sniffingu parametrów i słabych statystyk - w przypadku, gdy kod GUID został zakodowany w zapytaniu, optymalizator zapytań próbuje zoptymalizować zapytanie dla tej wartości parametru. Sądzę, że to samo dzieje się z parametryzowanym/przygotowanym zapytaniem (to się nazywa sniffowanie parametrów - plan wykonania jest zoptymalizowany pod kątem użytych parametrów przy pierwszym wykonaniu przygotowanej instrukcji), jednak na pewno nie stanie się, gdy zadeklarujesz parametr i użyj go w zapytaniu.

Tak jak powiedziałem, serwer SQL próbuje zoptymalizować plan wykonania: dla tej wartości, więc zazwyczaj powinieneś zobaczyć lepsze wyniki. Wydaje się, że informacje, na podstawie których podejmowane są decyzje, są nieprawidłowe/wprowadzające w błąd, a Ty lepiej (z jakiegoś powodu), gdy optymalizuje zapytanie o ogólną wartość parametru.

To jednak głównie domysły - nie da się powiedzieć naprawdę bez egzekucji - jeśli możesz gdzieś przesłać plan egzekucji, to jestem pewien, że ktoś będzie w stanie ci pomóc z prawdziwego powodu.

+0

Załadowałem plany wykonania, zobacz mój zredagowany post –

Powiązane problemy