Dieses Problem trat auf, als ich unterschiedliche Datensatzzählungen für identische Abfragen erhielt, von denen eine eine not in
where
Einschränkung und die andere eine Einschränkung verwendete left join
. Die Tabelle in der not in
Einschränkung hatte einen Nullwert (fehlerhafte Daten), wodurch diese Abfrage eine Anzahl von 0 Datensätzen zurückgab. Ich verstehe irgendwie warum, aber ich könnte Hilfe gebrauchen, um das Konzept vollständig zu verstehen.
Um es einfach auszudrücken: Warum gibt Abfrage A ein Ergebnis zurück, B jedoch nicht?
A: select 'true' where 3 in (1, 2, 3, null)
B: select 'true' where 3 not in (1, 2, null)
Dies war unter SQL Server 2005. Ich habe auch festgestellt, dass beim Aufruf von set ansi_nulls off
B ein Ergebnis zurückgegeben wird.
NOT IN
in eine Reihe von<> and
Änderungen das semantische Verhalten von nicht in dieser Menge auf etwas anderes ändert ?SELECT 1 WHERE NULL NOT IN (SELECT 1 WHERE 1=0);
eine Zeile anstelle der von mir erwarteten leeren Ergebnismenge ausgegeben wird.Wann immer Sie NULL verwenden, haben Sie es wirklich mit einer dreiwertigen Logik zu tun.
Ihre erste Abfrage gibt Ergebnisse zurück, wenn die WHERE-Klausel Folgendes ergibt:
Der zweite:
Das UNBEKANNTE ist nicht dasselbe wie FALSE. Sie können es einfach testen, indem Sie Folgendes aufrufen:
Beide Abfragen führen zu keinen Ergebnissen
Wenn UNBEKANNT dasselbe wie FALSCH wäre, müsste unter der Annahme, dass die erste Abfrage FALSCH ergibt, die zweite als WAHR ausgewertet werden, da es dasselbe wie NICHT (FALSCH) gewesen wäre.
Das ist nicht der Fall.
Es gibt einen sehr guten Artikel zu diesem Thema auf SqlServerCentral .
Die gesamte Ausgabe von NULLs und Three-Valued Logic kann zunächst etwas verwirrend sein, aber es ist wichtig zu verstehen, um korrekte Abfragen in TSQL zu schreiben
Ein weiterer Artikel, den ich empfehlen würde, ist SQL Aggregate Functions und NULL .
quelle
NOT IN
Gibt 0 Datensätze zurück, wenn sie mit einem unbekannten Wert verglichen werdenDa dies
NULL
unbekannt ist, gibt eineNOT IN
Abfrage, die einNULL
oderNULL
s in der Liste der möglichen Werte enthält, immer0
Datensätze zurück, da nicht sichergestellt werden kann, dass derNULL
Wert nicht der getestete Wert ist.quelle
Der Vergleich mit null ist undefiniert, es sei denn, Sie verwenden IS NULL.
Wenn Sie also 3 mit NULL vergleichen (Abfrage A), wird undefiniert zurückgegeben.
Dh SELECT 'true' wobei 3 in (1,2, null) und SELECT 'true' wo 3 nicht in (1,2, null)
führt zu demselben Ergebnis, da NOT (UNDEFINED) immer noch undefiniert, aber nicht TRUE ist
quelle
Der Titel dieser Frage zum Zeitpunkt des Schreibens lautet
Aus dem Text der Frage geht hervor, dass das Problem
SELECT
eher in einer SQL-DML- Abfrage als in einer SQL-DDL aufgetreten istCONSTRAINT
.Insbesondere angesichts des Wortlauts des Titels möchte ich jedoch darauf hinweisen, dass einige der hier gemachten Aussagen möglicherweise irreführende Aussagen sind, die in Anlehnung an (Paraphrasierung)
Obwohl dies bei SQL DML der Fall ist, ist der Effekt bei Berücksichtigung von Einschränkungen unterschiedlich.
Betrachten Sie diese sehr einfache Tabelle mit zwei Einschränkungen, die direkt den Prädikaten in der Frage entnommen wurden (und in einer ausgezeichneten Antwort von @Brannon angesprochen wurden):
Gemäß der Antwort von @ Brannon wird die erste Einschränkung (using
IN
) als TRUE und die zweite Einschränkung (usingNOT IN
) als UNKNOWN ausgewertet. Der Einsatz ist jedoch erfolgreich! Daher ist es in diesem Fall nicht unbedingt richtig zu sagen, "Sie erhalten keine Zeilen", da wir tatsächlich eine Zeile als Ergebnis eingefügt haben.Der obige Effekt ist in Bezug auf den SQL-92-Standard tatsächlich der richtige. Vergleichen und kontrastieren Sie den folgenden Abschnitt aus der SQL-92-Spezifikation
Mit anderen Worten:
In SQL DML werden Zeilen aus dem Ergebnis entfernt, wenn die
WHERE
Auswertung UNBEKANNT ist, da sie die Bedingung "ist wahr" nicht erfüllt.In SQL DDL (dh Einschränkungen) werden Zeilen nicht aus dem Ergebnis entfernt , wenn sie zu UNKNOWN ausgewertet werden, weil sie nicht die Bedingung erfüllen „nicht falsch ist“.
Obwohl die Auswirkungen in SQL DML bzw. SQL DDL widersprüchlich erscheinen mögen, gibt es einen praktischen Grund, UNBEKANNTEN Ergebnissen den "Vorteil des Zweifels" zu geben, indem sie eine Bedingung erfüllen können (genauer gesagt, damit sie eine Einschränkung nicht verfehlen können). : Ohne dieses Verhalten müssten alle Einschränkungen explizit mit Nullen umgehen, und das wäre aus Sicht des Sprachdesigns sehr unbefriedigend (ganz zu schweigen von einem richtigen Schmerz für Programmierer!).
ps Wenn Sie es als schwierig empfinden, einer Logik wie "Unbekannt erfüllt eine Einschränkung nicht" zu folgen, wie ich sie schreiben soll, sollten Sie darauf verzichten, indem Sie einfach nullbare Spalten in SQL DDL und alles in SQL vermeiden DML, die Nullen erzeugt (z. B. äußere Verknüpfungen)!
quelle
NOT IN (subquery)
Einbeziehen von Nullen zu unerwarteten Ergebnissen führen kann, ist es verlockend, dieseIN (subquery)
vollständig zu vermeiden und immer zu verwendenNOT EXISTS (subquery)
(wie ich es einmal getan habe!), Da es den Anschein hat, dass Nullen immer korrekt behandelt werden. Es gibt jedoch Fälle, in denenNOT IN (subquery)
das erwartete ErgebnisNOT EXISTS (subquery)
erzielt wird, während unerwartete Ergebnisse erzielt werden! Ich kann dies vielleicht noch aufschreiben, wenn ich meine Notizen zu diesem Thema finde (ich brauche Notizen, weil es nicht intuitiv ist!). Die Schlussfolgerung ist jedoch dieselbe: Vermeiden Sie Nullen!TRUE
,FALSE
undUNKNOWN
. Ich nehme an, 4.10 hätte lauten können: "Eine Tabellenprüfungsbeschränkung ist genau dann erfüllt, wenn die angegebene Suchbedingung für jede Zeile einer Tabelle WAHR oder UNBEKANNT ist" - beachten Sie meine Änderung am Ende des Satzes - die Sie weggelassen haben - - von "für jeden" bis "für alle". Ich habe das Bedürfnis, die logischen Werte zu kapitalisieren, weil sich die Bedeutung von "wahr" und "falsch" in der natürlichen Sprache sicherlich auf die klassische zweiwertige Logik beziehen muss.CREATE TABLE T ( a INT NOT NULL UNIQUE, b INT CHECK( a = b ) );
- Die Absicht hier ist, dassb
entweder gleicha
oder null sein muss. Wenn eine Einschränkung TRUE ergeben müsste, um erfüllt zu werden, müssten wir die Einschränkung ändern, um explizit Nullen zu behandeln, zCHECK( a = b OR b IS NULL )
. Daher müsste für jede Einschränkung...OR IS NULL
vom Benutzer eine Logik für jede betroffene nullfähige Spalte hinzugefügt werden: mehr Komplexität, mehr Fehler, wenn sie dies vergessen haben usw. Ich denke, das SQL-Standardkomitee hat nur versucht, pragmatisch zu sein.In A wird 3 auf Gleichheit mit jedem Mitglied der Menge getestet, was ergibt (FALSE, FALSE, TRUE, UNKNOWN). Da eines der Elemente WAHR ist, ist die Bedingung WAHR. (Es ist auch möglich, dass hier ein Kurzschluss auftritt, sodass er tatsächlich stoppt, sobald er die erste WAHR trifft und niemals 3 = NULL auswertet.)
In B wird die Bedingung meiner Meinung nach als NICHT bewertet (3 in (1,2, null)). Testen von 3 auf Gleichheit mit den festgelegten Ausbeuten (FALSE, FALSE, UNKNOWN), die zu UNKNOWN aggregiert werden. NOT (UNKNOWN) ergibt UNKNOWN. Insgesamt ist also die Wahrheit des Zustands unbekannt, was am Ende im Wesentlichen als FALSCH behandelt wird.
quelle
Aus den Antworten hier kann geschlossen werden, dass
NOT IN (subquery)
Nullen nicht richtig behandelt werden und zugunsten von vermieden werden solltenNOT EXISTS
. Eine solche Schlussfolgerung kann jedoch verfrüht sein. In dem folgenden Szenario, das Chris Date (Database Programming and Design, Band 2, Nr. 9, September 1989)NOT IN
gutgeschrieben wurde, werden Nullen korrekt behandelt und das richtige Ergebnis zurückgegeben, anstattNOT EXISTS
.Betrachten Sie eine Tabelle
sp
, um Lieferanten (sno
) darzustellen , von denen bekannt ist, dass sie Teile (pno
) in Menge (qty
) liefern . Die Tabelle enthält derzeit die folgenden Werte:Beachten Sie, dass die Menge nullbar ist, dh um erfassen zu können, dass ein Lieferant bekanntermaßen Teile liefert, auch wenn nicht bekannt ist, in welcher Menge.
Die Aufgabe besteht darin, die Lieferanten zu finden, deren Lieferteilnummer 'P1' bekannt ist, jedoch nicht in Mengen von 1000.
Folgendes wird verwendet
NOT IN
, um nur den Lieferanten 'S2' korrekt zu identifizieren:Die folgende Abfrage verwendet jedoch dieselbe allgemeine Struktur, enthält
NOT EXISTS
jedoch fälschlicherweise den Lieferanten 'S1' im Ergebnis (dh für den die Menge null ist):Also
NOT EXISTS
ist nicht die Silberkugel, die es erschienen sein könnte!Die Ursache des Problems ist natürlich das Vorhandensein von Nullen, daher besteht die "echte" Lösung darin, diese Nullen zu beseitigen.
Dies kann (neben anderen möglichen Designs) anhand von zwei Tabellen erreicht werden:
sp
Lieferanten, von denen bekannt ist, dass sie Teile liefernspq
Lieferanten, von denen bekannt ist, dass sie Teile in bekannten Mengen liefernBeachten Sie, dass es wahrscheinlich eine Fremdschlüsseleinschränkung geben sollte, auf die
spq
verwiesen wirdsp
.Das Ergebnis kann dann mit dem Vergleichsoperator 'minus' (der das
EXCEPT
Schlüsselwort in Standard SQL ist) erhalten werden, zquelle
Null bedeutet und das Fehlen von Daten, das heißt, es ist unbekannt, kein Datenwert von nichts. Es ist für Leute mit Programmierhintergrund sehr leicht, dies zu verwechseln, da in Sprachen vom Typ C bei Verwendung von Zeigern null in der Tat nichts ist.
Daher befindet sich 3 im ersten Fall tatsächlich in der Menge von (1,2,3, null), so dass true zurückgegeben wird
Im zweiten können Sie es jedoch auf reduzieren
Wählen Sie 'true', wobei 3 nicht in (null)
Es wird also nichts zurückgegeben, da der Parser nichts über die Menge weiß, mit der Sie es vergleichen - es ist keine leere Menge, sondern eine unbekannte Menge. Die Verwendung von (1, 2, null) hilft nicht weiter, da die Menge (1,2) offensichtlich falsch ist, aber dann tun Sie das gegen Unbekanntes, was unbekannt ist.
quelle
Wenn Sie mit NOT IN nach einer Unterabfrage filtern möchten, die NULL-Werte enthält, aktivieren Sie das Kontrollkästchen nicht null
quelle
das ist für Boy:
Dies funktioniert unabhängig von den Ansi-Einstellungen
quelle
SQL verwendet dreiwertige Logik für Wahrheitswerte. Die
IN
Abfrage erzeugt das erwartete Ergebnis:Das Hinzufügen von a
NOT
kehrt die Ergebnisse jedoch nicht um:Dies liegt daran, dass die obige Abfrage der folgenden entspricht:
So wird die where-Klausel bewertet:
Beachte das:
NULL
ErträgenUNKNOWN
OR
Ausdruck, bei dem keiner der OperandenTRUE
und mindestens ein Operand vorhanden ist,UNKNOWN
ergibtUNKNOWN
( ref )NOT
vonUNKNOWN
AusbeutenUNKNOWN
( ref )Sie können das obige Beispiel auf mehr als zwei Werte erweitern (z. B. NULL, 1 und 2), aber das Ergebnis ist dasselbe: Wenn einer der Werte ist,
NULL
stimmt keine Zeile überein.quelle
Dies kann auch hilfreich sein, um den logischen Unterschied zwischen Join, Exist und http://weblogs.sqlteam.com/mladenp/archive/2007/05/18/60210.aspx zu ermitteln
quelle