Czy jest oceniane zwarcie klauzuli SQL WHERE?

142

Czy wyrażenia boolowskie w klauzulach SQL WHERE są oceniane jako zwarte ?

Na przykład:

SELECT * 
FROM Table t 
WHERE @key IS NULL OR (@key IS NOT NULL AND @key = t.Key) 

Jeśli @key jest NULL wartość true, to @key IS NOT NULL AND @key = t.Key oceniane?

Jeśli nie, dlaczego nie?

Jeśli tak, czy jest to gwarantowane? Czy jest to część ANSI SQL, czy jest to specyficzne dla bazy danych?

Jeśli specyficzna dla bazy danych, SqlServer? Wyrocznia? MySQL?

Greg Dean
źródło
Czy klauzula @key NIE JEST NULL zbędna? Klauzula @key IS NULL na LHS załatwia to, nie?
wydający
10
@splender - zależy od odpowiedzi na pytanie
Greg Dean
@Greg: Zgadzam się ze spender. Nie widzę, aby brak lub obecność zwarcia robiły jakąkolwiek różnicę. Jeśli @key IS NULL, to @key = t.Key zawsze zwróci false, ponieważ NULL! = NULL (dlatego w końcu używamy IS NULL).
Michael Madsen
14
@Michael i @spender - Chodzi o to, czy drugi warunek jest oceniany, czy nie. Nie chodzi o to, czy ta konkretna instrukcja SQL jest napisana jak najmniejszą liczbą znaków. W bardziej skomplikowanych przykładach niewątpliwie miałoby to znaczenie, tak jakby klauzula where zwarła spięcia, można by było napisać wyrażenia, które w innym przypadku byłyby błędne.
Greg Dean
2
Zwarcie oznacza ocenę warunków od lewej do prawej. Biorąc pod uwagę taki warunek, jak WHERE a = 1 AND b = 2może być wydajne, aby silnik bazy danych znalazł najpierw wszystkie wiersze, w których b = 2, a następnie filtruj, gdzie a = 1. Jeśli poprosisz o gwarancję, optymalizator stanie się bezużyteczny.
Salman A,

Odpowiedzi:

72

ANSI SQL Draft 2003 5WD-01-Framework-2003-09.pdf

6.3.3.3 Kolejność oceny reguł

[…]

Tam, gdzie pierwszeństwo nie jest określone przez formaty lub nawiasy, efektywna ocena wyrażeń jest zwykle wykonywana od lewej do prawej. Jednak od implementacji zależy, czy wyrażenia są faktycznie oceniane od lewej do prawej, szczególnie gdy operandy lub operatory mogą powodować podniesienie warunków lub jeśli wyniki wyrażeń można określić bez całkowitego oceniania wszystkich części wyrażenia.

Społeczność
źródło
4
Zależne od implementacji? Wspaniały. Też dobrze wiedzieć. Przynajmniej CASEjest zwarty.
dakab
3
Czy to nie oznacza, że ​​oceny wyrażeń są źle zdefiniowane? „(0 = 0 LUB NULL)”, ma zawsze wartość NULL, jeśli wszystkie wyrazy są oceniane, ale zawsze prawdziwe, jeśli oceniane są od lewej do prawej i są zwarte.
user48956
7
SQL jest językiem deklaratywnym, w zasadzie wyraża logikę obliczenia bez opisywania jego przepływu sterowania; co w pewnym sensie jest sprzeczne z imperatywnym stylem oceny zwarć i jej konsekwencjami.
Jorge Garcia,
Nie myślałem o tym w ten sposób @JorgeGarcia. Wydaje mi się, że ocena zwarcia niejawnie wymusza porządek operacji. Zmagam się z kodem, który prawdopodobnie leży u podstaw subtelnego problemu. Dzięki za wgląd.
Carnot Antonio Romero
58

Z powyższego wynika, że ​​zwarcie nie jest naprawdę dostępne.

Jeśli potrzebujesz, proponuję oświadczenie Case:

Where Case when Expr1 then Expr2 else Expr3 end = desiredResult

Expr1jest zawsze oceniana, ale tylko jedna z nich Expr2i Expr3będzie oceniana na wiersz.

PMc
źródło
3
To zależy od implementacji RDBMS, którą zakładam. Przynajmniej dla SQL Server istnieje co najmniej jeden wyjątek, dla którego udokumentowano, że nie wykazuje tego zachowania (tj. Zwarcia); cf CASE (Transact-SQL) - Uwagi . Zacytowałem ten przypadek w tej odpowiedzi, którą podałem na pytanie Sql - jawna kolejność warunków GDZIE? .
TT.
1
Wyrażenie przypadku , nie instrukcja.
jarlh
19

Myślę, że to jeden z przypadków, w których napisałbym to tak, jakby nie powodowało zwarcia, z trzech powodów.

  1. Ponieważ w przypadku MSSQL nie można go rozwiązać, patrząc na BOL w oczywistym miejscu, więc dla mnie jest to kanonicznie niejednoznaczne.

  2. bo przynajmniej wtedy wiem, że mój kod będzie działał. A co ważniejsze, tak samo będzie z tymi, którzy przyjdą po mnie, więc nie będę ich zmuszać do martwienia się w kółko o to samo pytanie.

  3. Piszę wystarczająco często dla kilku produktów DBMS i nie chcę pamiętać różnic, jeśli mogę je łatwo obejść.

dkretz
źródło
4
Świetna sugestia. Nie odpowiada na pytanie, ale jest wielkim pragmatycznym punktem widzenia. więc +1
Greg Dean
12

Nie wierzę, że zwarcie w SQL Server (2005) jest gwarantowane. SQL Server przeprowadza zapytanie poprzez algorytm optymalizacji, który bierze pod uwagę wiele czynników (indeksy, statystyki, rozmiar tabeli, zasoby itp.), Aby opracować efektywny plan wykonania. Po tej ocenie nie można z całą pewnością stwierdzić, że logika zwarcia jest gwarantowana.

Sam kiedyś napotkałem to samo pytanie i moje badania naprawdę nie dały mi ostatecznej odpowiedzi. Możesz napisać małe zapytanie, aby dać ci poczucie, że to działa, ale czy możesz być pewien, że wraz ze wzrostem obciążenia bazy danych, tabele stają się większe, a rzeczy są optymalizowane i zmieniane w bazie danych, ten wniosek będzie utrzymać. Nie mogłem i dlatego popełniłem błąd po stronie ostrożności i użyłem CASE w klauzuli WHERE, aby zapewnić zwarcie.

Mehmet Aras
źródło
7

Musisz pamiętać, jak działają bazy danych. Biorąc pod uwagę sparametryzowane zapytanie, db buduje plan wykonania w oparciu o to zapytanie bez wartości parametrów. To zapytanie jest używane za każdym razem, gdy zapytanie jest uruchamiane, niezależnie od rzeczywistych podanych wartości. Plan wykonania nie ma znaczenia, czy w zapytaniu występują zwarcia z określonymi wartościami.

Logicalmind
źródło
6
ma znaczenie dla szybkości wykonania!
user4951
To, że obecnie tak to działa, nie oznacza, że ​​nie można go zmienić. Musimy oddzielić model / semantykę od implementacji. Plany wykonania są wdrażane wewnętrznie w celu optymalizacji wykonywania zapytań… a semantyka zwarć nie tylko zaprzecza deklaratywnej naturze SQL, ale może ograniczać takie optymalizacje. Jednak gdyby semantyka oceny zwarć była obsługiwana przez DBMS, implementacja planów wykonania zmieniłaby się, aby obsługiwać taką semantykę.
Jorge Garcia,
3

Zwykle używam tego dla parametrów opcjonalnych. Czy to to samo, co zwarcie?

SELECT  [blah]
FROM    Emp
WHERE  ((@EmpID = -1) OR (@EmpID = EmpID))

Daje mi to opcję przekazania -1 lub czegokolwiek, aby uwzględnić opcjonalne sprawdzenie atrybutu. Czasami wiąże się to z łączeniem się na wielu stołach lub najlepiej w widoku.

Bardzo poręczny, nie do końca pewny dodatkowej pracy jaką daje silnik db.

p.campbell
źródło
2

Myślę, że w przypadku SQL Server zależy to od wersji, ale moje doświadczenie z SQL Server 2000 polega na tym, że nadal ocenia on @key = t.Key, nawet jeśli @key ma wartość null. Innymi słowy, nie powoduje efektywnego zwarcia podczas oceny klauzuli WHERE.

Widziałem ludzi zalecających strukturę, taką jak twój przykład, jako sposób na wykonanie elastycznego zapytania, w którym użytkownik może wprowadzić lub nie wprowadzić różne kryteria. Moją obserwacją jest to, że Key jest nadal zaangażowany w plan kwerend, gdy @key ma wartość null, a jeśli klucz jest indeksowany, nie używa go wydajnie.

Ten rodzaj elastycznych zapytań z różnymi kryteriami jest prawdopodobnie jednym z przypadków, w których dynamicznie utworzony SQL jest naprawdę najlepszym rozwiązaniem. Jeśli @key ma wartość null, po prostu w ogóle go nie uwzględniasz w zapytaniu.

tetranz
źródło
2

Właśnie natknąłem się na to pytanie i już znalazłem ten wpis na blogu: http://rusanu.com/2009/09/13/on-sql-server-boolean-operator-short-circuit/

Serwer SQL może optymalizować zapytanie w dowolnym miejscu, które uzna za stosowne, więc w przykładzie podanym w poście na blogu nie można polegać na zwarciu.

Jednak CASE jest najwyraźniej udokumentowany w celu oceny w kolejności pisemnej - sprawdź komentarze do tego posta na blogu.

stolsvik
źródło
1

Główną cechą oceny zwarcia jest to, że przestaje ona oceniać wyrażenie, gdy tylko można określić wynik. Oznacza to, że resztę wyrażenia można zignorować, ponieważ wynik będzie taki sam, niezależnie od tego, czy jest oceniany, czy nie.

Binarne operatory logiczne są przemienne, co oznacza:

a AND b == b AND a
a OR  b == b OR  a
a XOR b == b XOR a

więc nie ma gwarancji kolejności oceny. Kolejność oceny zostanie określona przez optymalizator zapytań.

W językach z obiektami mogą wystąpić sytuacje, w których można napisać wyrażenia logiczne, które można ocenić tylko za pomocą oceny zwarcia. Twoja przykładowa konstrukcja kodu jest często używana w takich językach (C #, Delphi, VB). Na przykład:

if(someString == null | someString.Length == 0 )
  printf("no text in someString");

Ten przykład C # spowoduje wyjątek, jeśli someString == nullponieważ zostanie w pełni oceniony. W ocenie zwarć będzie działać za każdym razem.

SQL działa tylko na zmiennych skalarnych (bez obiektów), których nie można odinicjalizować, więc nie ma możliwości napisania wyrażenia boolowskiego, którego nie można ocenić. Jeśli masz jakąś wartość NULL, każde porównanie zwróci fałsz.

Oznacza to, że w SQL nie można napisać wyrażenia, które jest inaczej oceniane w zależności od zastosowania zwarcia lub pełnej oceny.

Jeśli implementacja SQL wykorzystuje ocenę zwarć, może tylko przyspieszyć wykonanie zapytania.

zendar
źródło
1
Tak, operatory logiczne są przemienne. Nie sądzę, żeby przedmioty (lub nie) miały z tym cokolwiek wspólnego.
Greg Dean
1

nie wiem o krótkim krążeniu, ale zapisałbym to jako stwierdzenie if-else

if (@key is null)
begin

     SELECT * 
     FROM Table t 

end
else
begin

     SELECT * 
     FROM Table t 
     WHERE t.Key=@key

end

również zmienne powinny zawsze znajdować się po prawej stronie równania. to czyni go możliwym do rozstrzygnięcia.

http://en.wikipedia.org/wiki/Sargable

DForck42
źródło
1
Czy ktoś może potwierdzić to o zmiennych po prawej stronie? Z jakiegoś powodu trudno mi w to uwierzyć.
Greg Dean
searchoracle.techtarget.com/expert/KnowledgebaseAnswer/ ... nie mogę teraz znaleźć nic innego
DForck42
Jak rozumiem artykuł. Mówi o funkcjach w nazwach kolumn, których nie można użyć. Co rozumiem. Nie sądzę jednak, żeby (A = @a) czy (@a = A) miało znaczenie.
Greg Dean
mogę się mylić. może być dobrym pytaniem, jeśli jeszcze nie istnieje.
DForck42
1

Poniżej szybki i brudny test na SQL Server 2008 R2:

SELECT *
FROM table
WHERE 1=0
AND (function call to complex operation)

Wraca natychmiast bez żadnych rekordów. Wystąpił rodzaj zachowania zwarciowego.

Następnie spróbowałem tego:

SELECT *
FROM table
WHERE (a field from table) < 0
AND (function call to complex operation)

wiedząc, że żaden rekord nie spełnia tego warunku:

(a field from table) < 0

Zajęło to kilka sekund, wskazując, że zachowanie zwarcia już nie występowało i złożona operacja była oceniana dla każdego rekordu.

Mam nadzieję, że to pomaga ludziom.

Jorge
źródło
1
Domyślam się, że pierwsze zapytanie zostało „zwarte” w czasie kompilacji, zanim faktycznie rozpoczęło się wykonywanie planu.
Louis Somers
1

Oto demo, które ma udowodnić, że MySQL wykonuje skracanie klauzuli WHERE :

http://rextester.com/GVE4880

Spowoduje to uruchomienie następujących zapytań:

SELECT myint FROM mytable WHERE myint >= 3 OR myslowfunction('query #1', myint) = 1;
SELECT myint FROM mytable WHERE myslowfunction('query #2', myint) = 1 OR myint >= 3;

Jedyną różnicą między nimi jest kolejność operandów w warunku OR.

myslowfunctioncelowo zasypia na sekundę i ma efekt uboczny polegający na dodawaniu wpisu do tablicy dziennika przy każdym uruchomieniu. Oto wyniki tego, co jest rejestrowane podczas uruchamiania dwóch powyższych zapytań:

myslowfunction called for query #1 with value 1
myslowfunction called for query #1 with value 2
myslowfunction called for query #2 with value 1
myslowfunction called for query #2 with value 2
myslowfunction called for query #2 with value 3
myslowfunction called for query #2 with value 4

Powyższe pokazuje, że powolna funkcja jest wykonywana więcej razy, gdy pojawia się po lewej stronie warunku LUB, gdy drugi operand nie zawsze jest prawdziwy (z powodu zwarcia).

Steve Chambers
źródło
4
Hmm, co prawdopodobnie miałeś na myśli "Oto demo, które ma udowodnić, że MySQL w tym konkretnym przypadku wykonuje zwarcie klauzuli WHERE :"
TT.
1
Jasne - to tylko dowód, że może się to zdarzyć.
Steve Chambers
0

Zajmuje to dodatkowe 4 sekundy w analizatorze zapytań, więc z tego, co widzę, nie jest nawet zwarte ...

SET @ADate = NULL

IF (@ADate IS NOT NULL)
BEGIN
    INSERT INTO #ABla VALUES (1)
        (SELECT bla from a huge view)
END

Byłoby miło mieć gwarantowany sposób!

niebieskawy
źródło
-2

Jest jednak oczywiste, że serwer MS Sql obsługuje teorię zwarć, aby poprawić wydajność, unikając niepotrzebnego sprawdzania,

Wspierający przykład:

SELECT 'TEST'
WHERE 1 = 'A'

SELECT 'TEST'
WHERE 1 = 1 OR 1 = 'A'

W tym przypadku pierwszy przykład spowodowałby błąd „Konwersja nie powiodła się podczas konwersji wartości varchar„ A ”na typ danych int.”

Podczas gdy drugi działa łatwo, ponieważ warunek 1 = 1 jest oceniany na PRAWDA, a zatem drugi warunek w ogóle nie działa.

Ponadto

SELECT 'TEST'
WHERE 1 = 0 OR 1 = 'A'

tutaj pierwszy warunek zostanie uznany za fałszywy i dlatego DBMS przejdzie do drugiego warunku i ponownie otrzymasz błąd konwersji, jak w powyższym przykładzie.

UWAGA: NAPISŁEM BŁĘDNY WARUNEK TYLKO, ABY ZREALIZOWAĆ POGODĘ STAN JEST WYKONANY LUB ZWARCIE, JEŚLI ZAPYTANIE WYNIKA BŁĄD OZNACZA STAN WYKONANY, ZWARCIOWY W INNY SPOSÓB.

PROSTE WYJAŚNIENIE

Rozważać,

WHERE 1 = 1 OR 2 = 2

ponieważ pierwszy warunek jest oceniany jako PRAWDA , nie ma sensu oceniać drugiego warunku, ponieważ jego ocena w jakiejkolwiek wartości nie wpłynęłaby w ogóle na wynik, więc jest to dobra okazja dla Sql Server, aby zaoszczędzić czas wykonania zapytania, pomijając niepotrzebne sprawdzanie lub ocenę warunków .

w przypadku „LUB”, jeśli pierwszy warunek ma wartość PRAWDA, cały łańcuch połączony przez „LUB” zostałby uznany za oceniony jako prawdziwy bez oceny innych.

condition1 OR condition2 OR ..... OR conditionN

jeśli warunek warunek1 zostanie oszacowany jako prawdziwy, wszystkie warunki spocznij, aż warunekN zostanie pominięty. W uogólnionych słowach przy określaniu pierwszego PRAWDA wszystkie inne warunki połączone przez OR zostaną pominięte.

Rozważ drugi warunek

WHERE 1 = 0 AND 1 = 1

ponieważ pierwszy warunek jest oceniany na FAŁSZ oceniany nie ma sensu oceniać drugiego warunku, ponieważ jego ocena w jakiejkolwiek wartości nie wpłynęłaby w ogóle na wynik, więc ponownie jest to dobra okazja dla Sql Server, aby zaoszczędzić czas wykonania zapytania, pomijając niepotrzebne sprawdzanie lub ocenę stanu .

w przypadku „AND”, jeśli pierwszy warunek ma wartość FALSE, cały łańcuch połączony z „AND” zostałby uznany za oszacowany na FALSE bez oceny innych.

condition1 AND condition2 AND ..... conditionN

jeśli condition1 oceniana jest na FAŁSZ , reszta wszystkie warunki till conditionN będzie pominięty. W uogólnionych słowach przy określaniu pierwszego FAŁSZ , wszystkie inne warunki połączone operatorem AND zostaną pominięte.

Dlatego mądry programista powinien zawsze programować łańcuch warunków w taki sposób, aby mniej kosztowny lub najbardziej eliminujący warunek był oceniany w pierwszej kolejności, lub tak ustalał stan w taki sposób, który przynosiłby maksymalne korzyści

RkHirpara
źródło
Powód negatywny: zawsze testuj rzeczy na prawdziwym serwerze z realistycznymi danymi. Wygląda na to, że mój poprzedni komentarz został zjedzony.
Jasmine