2009-05-13 14 views
16

Ich war neulich eine gespeicherte Prozedur Debuggen und eine gewisse Logik etwas wie folgt gefunden:„<>“ vs „NOT IN“

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

Das ist nichts zurückgegeben. Ich dachte, dass es mit der "<>" etwas seltsam aussah, also änderte ich es auf "NICHT IN" und dann hat alles gut funktioniert. Ich habe mich gefragt, warum das so ist? Dies ist ein ziemlich alter Prozess und ich bin mir nicht sicher, wie lange das Problem schon existiert, aber wir haben kürzlich von SQL Server 2005 auf SQL Server 2008 gewechselt, als dies entdeckt wurde. Was ist der wirkliche Unterschied zwischen "<>" und "NOT IN" und hat sich das Verhalten zwischen Server2005 und 2008 geändert?

Antwort

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

überprüft jeden Wert in der Liste.

Allerdings ist NOT IN nicht NULL-tolerant. Wenn die Unterabfrage eine Reihe von Werten zurückgibt, die NULL enthalten, werden überhaupt keine Datensätze zurückgegeben. (Dies liegt daran, dass intern der NOT IN auf idcode <> 'foo' AND idcode <> 'bar' AND idcode <> NULL usw. optimiert ist, was immer fehlschlägt, da jeder Vergleich mit NULL UNBEKANNT ergibt, was verhindert, dass der gesamte Ausdruck jemals WAHR wird.)

Eine schönere, NULL-tolerante Variante wäre dies:

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

EDIT: ich zunächst davon ausgegangen, dass dies:

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

nur gegen den ersten Wert überprüfen würde. Es stellt sich heraus, dass diese Annahme zumindest für SQL Server falsch ist, wo es sein Fehler tatsächlich auslöst:

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

'WO ID-Code NICHT IN (...)' ist äquivalent zu 'WHERE ID-Code <> ALL (...)' –

7

<> ist ein "singular" NOT Betrieb; NOT IN ist eine Set-Operation, also macht es Sinn, dass ersteres nicht funktionieren würde. Ich habe keine Ahnung, ob dies unter einer früheren Version von SQL Server geschehen ist.

+0

Es ist nie in SQL Server anders gemacht wurde. – Tomalak

11

dies versuchen, weil der Indexnutzung kann schneller laufen:

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

+1 gute Alternative –

+0

Das ist eine gute Idee! –

+0

SQL Server normalerweise (ich habe keinen Fall gesehen, wenn dies nicht der Fall ist) erzeugt identische QEPs für eine Join oder eine Unterabfrage und daher identische Leistung. – pipTheGeek

2

Ich habe keine Ahnung Warum würdest du sowas wie WHERE idcode <> (SELECT ids FROM tmpIdTable) schreiben? Eine SELECT-Anweisung gibt eine Reihe von Tupeln zurück, und Ihr ID-Code wird oder wird NICHT IN diesem Satz sein. "WHERE idcode NOT IN (SELECT ids FROM tmpIdTable)" ist der Weg, es zu tun.

+0

Deshalb wusste ich, um es zu beheben, ich wusste nur nicht genau, was die Logik dahinter war. –

+0

Die <> -Anweisung kann sinnvoll sein, wenn die Unterabfrage auf irgendeine Weise geordnet ist, z. "<> das größte [Ding] in der Liste". – Tomalak

+0

Sie haben recht, wenn die Unterabfrage geordert wird, könnte die <> -Notation Sinn machen, aber ich würde sie trotzdem vermeiden, da sie fehleranfällig ist - leicht übersehen.Wer später die Reihenfolge der Unterabfrage entfernt oder ändert, kann darüber verblüfft sein, warum die Abfrage nichts zurückgibt. –

-1

in einigen Versionen von SQL ! = sollte für eine logische Anweisung "nicht gleich" verwendet werden. Haben Sie das versucht?

+2

'<>' und '! =' Sind auf SQL Server gleichwertig. Es gibt keine Version, die auf '! =' Besteht. Aber '<>' ist der ANSI SQL-Standard, um dies zu tun, obwohl ich es aus irgendeinem Grund persönlich häufiger benutze! =. – Tomalak

4

Dieser Code ist gültig, wenn und nur wenn es keine Zeilen oder eine einzelne Reihe von tmpIdTable zurückgegeben:

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

Wenn mehrere Zeilen zurückgegeben werden, erhalten Sie einen Fehler wie erhalten:

Msg 512 , Ebene 16, Status 1, Zeile 1 Unterabfrage gab mehr als 1 Wert zurück. Dies ist nicht zulässig, wenn die Unterabfrage folgt =,! =, <, < =,>,> = oder wenn die Unterabfrage als Ausdruck verwendet wird.

Dies ist der gleiche Fehler, den Sie mit verschachteltem Skalare bekommen unerwartet mehrere Zeilen wie produziert:

SELECT *, (SELECT blah FROM t1 WHERE etc.) von T2

Nichts hat WRT dies in SQL Server geändert in einem Jahrzehnt, so erwarte ich, dass Annahmen über die geschachtelte Abfrage im ursprünglichen Code gebrochen wurden. Wenn keine Zeilen zurückgegeben werden, ist das Ergebnis leer, da <> NULL niemals wahr ist (angenommen ANSI NULL).

Dieser Code ist für eine beliebige Anzahl von Zeilen gültig:

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

Allerdings gibt es immer noch Probleme mit NULL sein kann.

+0

Ich hatte dieses Gespräch heute Morgen mit einem Kollegen. Es ist ähnlich, aber nicht ganz die Antwort. –

+0

Konzeptionell zumindest ist es sogar falsch, wenn der Subselect Null oder eine Zeile zurückgibt. Weil Sie fragen, ob ein Skalar, ID-Code, gleich einer Null- oder Ein-Element-Liste ist. Der Skalar ist niemals wirklich gleich der Liste, selbst wenn er gleich dem einzigen Element in einer Ein-Element-Liste ist. –

+0

Tatsächlich benutze ich das Konstrukt nie und ich würde es normalerweise als einen Code-Geruch betrachten. –

2

Wenn die SELECT-Unterabfrage null Zeilen zurückgibt, ist das ein NULL. Wenn NULL mit irgendetwas verglichen wird, ist das Ergebnis immer UNBEKANNT und niemals WAHR. Verwirrenderweise ist NOT UNKNOWN gleich UNKNOWN.

Ich vermeide wann immer möglich dreiwertige Logik (TRUE, FALSE, UNKNOWN). Es ist nicht schwer zu vermeiden, sobald Sie den Dreh raus haben.

Wenn die SELECT-Unterabfrage genau einen Wert zurückgibt, sollte der Vergleich für die Ungleichung das erwartete Ergebnis liefern.

Wenn die SELECT-Unterabfrage mehr als einen Wert zurückgibt, sollte ein Fehler auftreten.

Im Allgemeinen gibt NOT IN das Ergebnis zurück, das Sie erwarten, wenn Sie für Nicht-Mitgliedschaft in einem Set testen.

Diese Antwort überschneidet sich mit anderen Antworten, aber sie ist etwas anders formuliert.

Edited mehr Details über NICHT IN hinzuzufügen:

Ich habe einige über NICHT in Oracle gesucht, und ich lernte etwas, das ich nicht vor einer halben Stunde wusste. NOT IN ist NULL-sensitiv. Insbesondere

X NOT IN (SELECT ...) 

Ist das nicht das gleiche wie

NOT (X IN SELECT ...)) 

Ich muss kann meine frühere Antwort ändern!

+0

+1 für das Aufzeigen des TRUE/FALSE/UNKNOWN-Teils. Vielen Dank. – Tomalak