2009-05-13 18 views
16

byłem debugowania procedury przechowywanej drugi dzień i znaleźć jakiś logiczny coś takiego:„<>” vs „NOT IN”

SELECT something 
FROM someTable 
WHERE idcode <> (SELECT ids FROM tmpIdTable) 

Ten zwrócony nic. Pomyślałem, że to trochę dziwne z "<>", więc zmieniłem go na "NIE IN", a następnie wszystko działało dobrze. Zastanawiam się, dlaczego tak jest? To jest całkiem stary proces i nie jestem do końca pewien, jak długo trwa ten problem, ale niedawno przełączyliśmy się z SQL Server 2005 na SQL Server 2008, kiedy to zostało odkryte. Jaka jest prawdziwa różnica między "<>" i "NOT IN" i czy zachowanie zmieniło się pomiędzy Server2005 a 2008?

Odpowiedz

18
SELECT something 
FROM someTable 
WHERE idcode NOT IN (SELECT ids FROM tmpIdTable) 

sprawdza przed jakąkolwiek wartością z listy.

Jednak wartość NOT IN nie jest tolerancyjna dla wartości NULL. Jeśli pod-zapytanie zwróciło zestaw wartości, które zawierały wartość NULL, żadne rekordy nie byłyby w ogóle zwracane. (To dlatego wewnętrznie NOT IN jest zoptymalizowany do idcode <> 'foo' AND idcode <> 'bar' AND idcode <> NULL itp, które zawsze zakończy się niepowodzeniem, ponieważ jakiekolwiek porównanie NULL plony nieznany, zapobiegając całe wyrażenie z kiedykolwiek staje PRAWDA.)

Ładniejszym wariant NULL-tolerancyjny byłoby to:

SELECT something 
FROM someTable 
WHERE NOT EXISTS (SELECT ids FROM tmpIdTable WHERE ids = someTable.idcode) 

EDIT: I początkowo założyć, że w ten sposób:

SELECT something 
FROM someTable 
WHERE idcode <> (SELECT ids FROM tmpIdTable) 

by sprawdzić przeciwko tylko pierwsza wartość. Okazuje się, że to założenie jest błędne przynajmniej dla SQL Server, gdzie rzeczywiście wyzwala swój błąd:

Msg 512, Level 16, State 1, Line 1 
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression. 
+1

'GDZIE idcode NIE IN (...)' jest równoważne 'WHERE idcode <> ALL (...)' –

7

<> jest operacją "pojedynczej" NOT; NOT IN to operacja ustawiona, więc ma sens, że ta pierwsza nie działałaby. Nie mam pojęcia, czy mogło to zrobić w poprzedniej wersji programu SQL Server.

+0

To nigdy nie zostało zrobione inaczej w SQL Server. – Tomalak

11

Spróbuj tego, może działać szybciej z powodu użycia index:

SELECT something 
FROM someTable 
    LEFT OUTER JOIN tmpIdTable ON idcode=ids 
WHERE ids IS NULL 
+0

+1 dobra alternatywa –

+0

To dobry pomysł! –

+0

SQL Server zwykle (nie widziałem przypadku, gdy nie ma) produkuje identyczne QEP dla łączenia lub kwerendy podrzędnej, a więc identycznej wydajności. – pipTheGeek

2

nie mam pojęcia dlaczego napiszesz coś w stylu: WHERE idcode <> (SELECT ids FROM tmpIdTable). Instrukcja SELECT zwróci zestaw krotek, a Twój identyfikator będzie albo NIE będzie w tym zestawie. "WHERE idcode NOT IN (SELECT ids FROM tmpIdTable)" to sposób na zrobienie tego.

+0

Właśnie dlatego wiedziałem, żeby to naprawić, po prostu nie wiedziałem dokładnie, jaka jest logika. –

+0

Instrukcja <> może mieć sens, jeśli zapytanie podrzędne jest w pewien sposób sortowane, np. "<> największa [rzecz] na liście". – Tomalak

+0

Masz rację, na wypadek gdyby zapytanie było uporządkowane, notacja <> mogłaby mieć jakiś sens, ale i tak by tego uniknąć, ponieważ jest podatny na błędy - łatwo przeoczyć jego intencje.Ktokolwiek usunie lub zmieni kolejność zapytań później, może być zaskoczony, dlaczego zapytanie nic nie zwraca. –

-1

w niektórych wersjach SQL ! = należy używać w przypadku stwierdzenia logicznego "nie równa się". Czy próbowałeś tego?

+2

"<>" i "! =" Są równoważne na SQL Server. Nie ma wersji, która kładzie nacisk na "! =". Ale "<>" jest standardem ANSI SQL, ale osobiście często używam! = Częściej z jakiegoś powodu. – Tomalak

4

Ten kod jest ważny tylko wtedy, gdy nie istnieją żadne wiersze lub jeden wiersz wrócił z tmpIdTable:

SELECT something 
FROM someTable 
WHERE idcode <> (SELECT ids FROM tmpIdTable) 

Jeśli są zwracane wiele wierszy, otrzymasz błąd jak:

Msg 512 , Level 16, State 1, Line 1 Podzapytanie zwróciło więcej niż jedną wartość. Jest to niedozwolone, gdy podzapytanie następuje po =,! =, <, < =,>,> = lub gdy podzapytanie jest używane jako wyrażenie.

Jest to ten sam błąd można uzyskać z zagnieżdżonego skalara niespodziewanie wywołuje wiele wierszy jak:

select *, (SELECT bla FROM t1 WHERE itp) FROM t2

Nic się nie zmieniło WRT to w SQL Server w ciągu dekady, więc spodziewam się, że założenia dotyczące zagnieżdżonego zapytania w oryginalnym kodzie zostały zerwane.

Jeśli nie zostaną zwrócone żadne wiersze, wynik będzie pusty, ponieważ <> NULL nigdy nie jest prawdziwa (załóżmy ANSI NULL).

Kod ten jest ważny dla dowolnej liczby wierszy:

SELECT something 
FROM someTable 
WHERE idcode NOT IN (SELECT ids FROM tmpIdTable) 

Jednak nadal mogą być problemy z NULL.

+0

Właściwie to miałem dziś rano tę samą rozmowę ze współpracownikiem. Jest to podobna, ale nie do końca odpowiedź. –

+0

Pojęciowo przynajmniej jest to błędne, jeśli podselekcja zwraca zero lub jeden wiersz. Ponieważ pytasz, czy skalar, idcode, jest równy zerowej lub jednoelementowej liście. Skalar nigdy nie jest tak naprawdę równy liście, nawet jeśli jest równy jedynemu elementowi na liście jednoelementowej. –

+0

Rzeczywiście, nigdy nie używam tego konstruktu i zwykle myślę o nim jako o zapachu kodu. –

2

Jeśli podkwerenda SELECT zwraca zero wierszy, jest to wartość NULL. Gdy NULL jest porównywany do czegokolwiek, wynik jest zawsze NIEZNANY, a nigdy PRAWDA. Mylące, NIE NIEZNANE jest równe NIEZNANE.

Unikałem trzech wartościowań logicznych (TRUE, FALSE, UNKNOWN), kiedy tylko jest to możliwe. Nie jest trudno uniknąć tego, gdy już to zrozumiesz.

Jeśli podkwerenda SELECT zwraca dokładnie jedną wartość, porównanie nierówności powinno zwrócić oczekiwany wynik.

Jeśli podzapytanie SELECT zwróci więcej niż jedną wartość, powinien pojawić się błąd.

Ogólnie rzecz biorąc, NOT IN zwraca wynik, którego oczekujesz, gdy testujesz na brak członkostwa w zestawie.

Ta odpowiedź pokrywa się z innymi odpowiedziami, ale jest sformułowana nieco inaczej.

Edited by dodać więcej szczegółów na temat NOT IN:

Zrobiłem kilka poszukiwania o NOT IN w Oracle, a ja nauczyłem się czegoś nie wie pół godziny temu. NOT IN ma wartość NULL. W szczególności

X NOT IN (SELECT ...) 

nie jest taki sam jak

NOT (X IN SELECT ...)) 

może muszę zmienić moje wcześniejsze odpowiedzi!

+0

+1 za wskazanie części TRUE/FALSE/UNKNOWN. Dzięki. – Tomalak