2010-01-29 11 views
5

Zastanawiam się, czy istnieje jakiś rozsądny sposób przepisania następującego zapytania, aby indeksy kolumn były używane przez optymalizator?Jak zoptymalizować użycie klauzuli "LUB" podczas używania z parametrami (SQL Server 2008)

Create Procedure select_Proc1 
    @Key1 int=0, 
    @Key2 int=0 
As 
BEGIN 
    Select key3 
    From Or_Table 
    Where (@key1 =0 OR Key1 [email protected]) AND 
      (@key2 =0 OR Key2 [email protected]) 
END 
GO 

Chociaż kolumny klauzul Gdzie są objęte indeksów, SQL Server nie jest w stanie korzystać z tych indeksów. Rodzi to pytanie, czy cokolwiek "blokuje" korzystanie z indeksów. Odpowiedź na to pytanie brzmi "tak" - sprawcami są parametry i warunek "OR". Parametry nie są uwzględnione w indeksach, co oznacza, że ​​SQL Server nie może użyć żadnego z indeksów do oceny "@ klucz1 = 0" (warunek, który dotyczy także klucza @ 2 = 0). Oznacza to, że SQL Server nie może używać indeksów do oceny klauzuli "@ klucz1 = 0 OR klucz1 = klucz @ 1" (ponieważ klauzula "OR" to połączenie wierszy objętych dwoma warunkami). Ta sama zasada odnosi się również do drugiej klauzuli (re. Key2). To prowadzi SQL Server do wniosku, że żadne indeksy nie mogą być użyte do wyodrębnienia wierszy, pozostawiając SQL Server do wykorzystania następnego najlepszego podejścia - klastrowany skan indeksu

Jak widać, optymalizator SQL nie użyje indeksów w kolumnach jeśli predykaty są "LUB" w klauzuli WHERE. Jednym z rozwiązań tego problemu jest oddzielenie zapytań klauzulą ​​FI dla wszystkich możliwych kombinacji parametrów.

Proszę przeczytać ten krótki artykuł, aby uzyskać lepszy widok problemu: http://www.sql-server-performance.com/articles/per/optimize_or_clause_p1.aspx

Teraz moje pytanie, co powinniśmy zrobić, jeśli możliwe kombinacje są bardziej, że tylko trzy lub cztery? Pisanie oddzielnego zapytania dla każdej kombinacji nie wydaje się racjonalnym rozwiązaniem. Czy istnieje inne obejście tego problemu?

Odpowiedz

11

SQL Server nie jest zbyt dobry w optymalizacji predykatów OR.

Użyj tego:

SELECT key3 
FROM or_table 
WHERE @key1 = 0 
     AND @key2 = 0 
UNION ALL 
SELECT key3 
FROM or_table 
WHERE @key1 = 0 
     AND @key2 <> 0 
     AND key2 = @key2 
UNION ALL 
SELECT key3 
FROM or_table 
WHERE @key2 = 0 
     AND @key1 <> 0 
     AND key1 = @key1 
UNION ALL 
SELECT key3 
FROM or_table 
WHERE @key1 <> 0 
     AND @key2 <> 0 
     AND key1 = @key1 
     AND key2 = @key2 

SQL Server będzie wyglądać z wartościami zmiennych przed wykonaniem zapytania i zoptymalizuje zbędnych zapytań na zewnątrz.

Oznacza to, że faktycznie zostanie wykonane tylko jedno zapytanie złożone z czterech.

+0

Ciekawe podejście, będę musiał o tym pamiętać. Przypuszczam, że miałoby to również zastosowanie do innych RDBMS, takich jak Oracle, gdzie 'OR' jest strasznie nieefektywne. – Lucero

+1

Czy to jedyny sposób? Co się stanie, jeśli zapytanie będzie bardziej skomplikowane (dotyczy to więcej klauzul OR)? – Meysam

3

MSSQL 2008 ma optymalizacji składnię stan uproszczenia, tutaj jest

Where (@key1 =0 OR Key1 [email protected]) AND 
     (@key2 =0 OR Key2 [email protected]) option(recompile) 

ten zoptymalizuje wykorzystanie stałych

+0

Nie sądzę, żeby to pomogło. Działa to tylko wtedy, gdy mamy do czynienia z problemem wąchania parametrów. – Meysam

2

Czy próbuje się tabela funkcji cenione?

CREATE FUNCTION select_func1 ( 
    @Key1 int=0, 
    @Key2 int=0 
) 
RETURNS TABLE 
AS RETURN (
    Select key3 
    From Or_Table 
    Where (@key1 =0 OR Key1 [email protected]) AND 
      (@key2 =0 OR Key2 [email protected]) 
) 


select * from select_func1(1,2) 
+0

OMG! Jak widzę w planie wykonania, indeksy są używane przy korzystaniu z funkcji wycenionej w tabeli! Jaka jest różnica?! Czy mógłbyś to wyjaśnić ?! – Meysam

+0

Funkcja wyceniana w tabeli jest oceniana za każdym razem, gdy jest używana (jak widok). Oznacza to, że optymalizator będzie w stanie ocenić parametry, zanim utworzy plan zapytania. Możesz zobaczyć funkcje wycenione w tabeli jako zaawansowane makra tekstowe w zapytaniu zewnętrznym. – adrianm

+0

Ups, problem z podejściem nadal występuje. Jeśli zamienisz parametry stałe w wywołaniu funkcji na zmienne parametry, ponownie indeksy nie będą używane. coś takiego: wybierz * from select_func1 (@ key1, @ key2) – Meysam

0

Tak - ostrożny wykorzystanie dynamicznego SQL rozwiąże ten problem. Można to zrobić na dwa sposoby:

a. Jeśli jesteś "purystą" o przechowywanych procach, skomponuj niestandardowy ciąg zapytania wewnątrz przechowywanego proc i uruchom ciąg znaków. Zapytanie szczegółowe może być następnie napisane dynamicznie dla każdego wykonania, aby uwzględnić tylko odpowiednie kryteria.

b. Jeśli jesteś elastyczny co do lokalizacji tego kodu SQL, możesz (ponownie UWAŻNIE) skomponować ciąg zapytania w swojej aplikacji i przekazać go do serwera.

Niebezpieczeństwo, oczywiście, dotyczy iniekcji SQL. Musisz więc bardzo uważać, w jaki sposób dane przekazywane są z klienta do dynamicznej instrukcji sql.

Kompleksowe informacje od Erland Sommarskog: http://www.sommarskog.se/dynamic_sql.html i http://www.sommarskog.se/dyn-search.html

Powiązane problemy