Zmusić program SQL Server do uruchomienia warunków zapytania zgodnie z opisem?

14

Używam SQL Server 2008 R2 i mam tę pseudo-zapytanie (SP):

select ...
from ...
WHERE    @LinkMode IS NULL
     AND (myColumn IN (...very long-running query...))
     ...
     ...

Problem polega na tym, że zapytanie zajmuje bardzo dużo czasu - nawet jeśli uruchomię SP za pomocą @LinkMode=2.

Jak zauważyłeś, długo działające zapytanie powinno być wykonane tylko wtedy, gdy @LinkMode ma wartość null, co nie ma miejsca w tym przypadku. W moim przypadku @LinkMode = 2!

Jeśli jednak zmienię to na:

 select ...
    from ...
    WHERE    1=2
         AND (myColumn IN (...very long time exeted query...))
     ...
     ...

SP działa szybko.

Słyszałem wcześniej, że czasami optymalizator może zoptymalizować kolejność kryteriów.

Więc pytam :

  • Nawet jeśli optymalizator wybierze inną trasę, co może być szybsze niż sprawdzenie, czy =null? Myślę, że sprawdzanie if a==nulljest znacznie szybsze niż uruchamianie innych długich zapytań ...

  • Jak zmusić program SQL Server do uruchomienia zapytania w takiej formie, w jakiej go napisałem (ta sama kolejność)?

Royi Namir
źródło

Odpowiedzi:

22

Wpadasz w pułapkę „ Catch-All Query ”, którą bardzo dobrze wyjaśnia Gail Shaw .

Podsumowując problem: SQL Server optymalizuje znaczne obciążenie kompilacji zapytań, buforując plan zapytań po kompilacji, a następnie sprawdzając w pamięci podręcznej odpowiedni plan zapytań przed późniejszą kompilacją. Występujące tutaj „dopasowanie” ma charakter czysto tekstowy, więc rzeczywista wartość zmiennej nie wpłynie na to.

To dobrze 99% czasu, ale w niektórych przypadkach jest źle . Przykładem może być sytuacja, gdy ktoś próbuje skonstruować klauzulę WHERE, tak jakby to było jak zwarcie instrukcji IF w C itp. To nie działa dobrze, ponieważ kompilator SQL musi stworzyć jeden plan zapytań, który będzie działał niezależnie rzeczywistych wartości parametrów i jedynym sposobem, w jaki może poradzić sobie z tymi „sprytnymi” logicznymi warunkami przełączania w klauzuli WHERE, jest stworzenie prostego planu brutalnej siły, który po prostu skanuje całą tabelę, filtrując wiersze w miarę jej przechodzenia , bez wykorzystywania żadnych indeksów.

Nic dziwnego, że powoduje to, że są one jednolicie wolne, bez względu na wartości parametrów / zmiennych.

RBarryYoung
źródło
8

Nie ma gwarantowanego sposobu zmuszenia serwera SQL do wykonania warunków klauzuli w określonej kolejności. Optymalizator zawsze ocenia je w kolejności, w jakiej uzna to za stosowne.

Możesz zrobić coś takiego:

IF @LinkMode IS NULL
BEGIN
    select ...
    from ...
    WHERE (myColumn IN (...very long time exeted query...))
         ...
         ...
END
ELSE
BEGIN
    select ...
    from ...
    WHERE ...
         ...
END
Nazwa ezoteryczna
źródło
3

Jeśli jest to opcja, użyj instrukcji JEŻELI, aby wykonać odpowiednią formę zapytania. Ponadto w SQL mówisz silnikowi db, co robić, a nie jak to zrobić - rzeczy nie są wykonywane od początku do końca. Trudno jest przewidzieć, co dokładnie zrobi. Prawdopodobnie o tym wiesz;)

Sam
źródło
2

Dynamiczny SQL prawdopodobnie też by działał, ponieważ w takim przypadku optymalizator zapytań powinien uzyskać rzeczywiste wartości w czasie wykonywania (popraw mnie, jeśli się mylę, w rzeczywistości nie jestem pewien, ale wydaje mi się, że pamiętam używanie go w podobnych sytuacjach) . Ale ja jestem z innymi w tym, że klauzula IF / ELSE będzie ci najlepiej służyć, ponieważ jest to najprostsze i najłatwiejsze rozwiązanie, które zrobi dokładnie to, co jest potrzebne.

W celu wykorzystania w przyszłości, jeśli jeszcze go nie używałeś, okropnie brzydka witryna z działającym przykładem dynamicznego SQL można znaleźć na przykład: http://sqlusa.com/bestpractices/dynamicsql/

Kahn
źródło
1

Poleciłbym konstrukcję IF / ELSE. Jeśli z jakiegokolwiek powodu, który Ci nie odpowiada, zawsze możesz rozważyć użycie opcji Z RECOMPILE ..

timvw
źródło
Czy mógłbyś rozwinąć kwestię tego, jak może wyglądać konstrukcja „if / else”? : D
jcolebrand
Chciałem zasugerować użycie OPCJI (Z RECOMPILE), ponieważ za każdym razem wygenerowałoby to idealny plan - opóźnienie kompilacji spowodowałoby dodatkowe obciążenie, ale podejrzewam, że ogólnie lepiej w tym przypadku.
SqlRyan