Haftungsausschluss: Ich habe das Problem herausgefunden (glaube ich), aber ich wollte dieses Problem zu Stack Overflow hinzufügen, da ich es (leicht) nirgendwo finden konnte. Außerdem könnte jemand eine bessere Antwort haben als ich.
Ich habe eine Datenbank, in der eine Tabelle "Common" von mehreren anderen Tabellen referenziert wird. Ich wollte sehen, welche Datensätze in der Common-Tabelle verwaist waren (dh keine Referenzen aus einer der anderen Tabellen hatten).
Ich habe diese Abfrage ausgeführt:
select *
from Common
where common_id not in (select common_id from Table1)
and common_id not in (select common_id from Table2)
Ich weiß, dass es verwaiste Aufzeichnungen gibt, aber keine Aufzeichnungen zurückgegeben wurden. Warum nicht?
(Dies ist SQL Server, wenn es darauf ankommt.)
sql
sql-server
tsql
Jeremy Stein
quelle
quelle
Antworten:
Aktualisieren:
Diese Artikel in meinem Blog beschreiben die Unterschiede zwischen den Methoden ausführlicher:
NOT IN
vs.NOT EXISTS
vsLEFT JOIN / IS NULL
.:SQL Server
NOT IN
vs.NOT EXISTS
vsLEFT JOIN / IS NULL
.:PostgreSQL
NOT IN
vs.NOT EXISTS
vsLEFT JOIN / IS NULL
.:Oracle
NOT IN
vs.NOT EXISTS
vsLEFT JOIN / IS NULL
.:MySQL
Es gibt drei Möglichkeiten, eine solche Abfrage durchzuführen:
LEFT JOIN / IS NULL
::NOT EXISTS
::NOT IN
::Wann
table1.common_id
nicht nullbar, sind alle diese Abfragen semantisch gleich.Wenn es nullbar ist,
NOT IN
ist es anders, daIN
(und daherNOT IN
) zurückgegeben wird,NULL
wenn ein Wert mit nichts in einer Liste übereinstimmt, die a enthältNULL
.Dies mag verwirrend sein, kann aber offensichtlicher werden, wenn wir uns an die alternative Syntax erinnern:
Das Ergebnis dieser Bedingung ist ein boolesches Produkt aller Vergleiche innerhalb der Liste. Natürlich
NULL
ergibt ein einzelner Wert dasNULL
Ergebnis, das auch das gesamte ErgebnisNULL
wiedergibt.Wir können niemals definitiv sagen, dass
common_id
dies nichts aus dieser Liste entspricht, da mindestens einer der Werte istNULL
.Angenommen, wir haben diese Daten:
LEFT JOIN / IS NULL
undNOT EXISTS
wird zurückkehren3
,NOT IN
wird nichts zurückgeben (da es immer entwederFALSE
oder auswerten wirdNULL
).In
MySQL
, falls in nicht nullbaren Spalten,LEFT JOIN / IS NULL
undNOT IN
sind ein wenig (mehrere Prozent) effizienter alsNOT EXISTS
. Wenn die Spalte nullbar ist,NOT EXISTS
ist sie am effizientesten (wiederum nicht viel).In ergeben
Oracle
alle drei Abfragen die gleichen Pläne (anANTI JOIN
).In
SQL Server
,NOT IN
/NOT EXISTS
sind effizienter, daLEFT JOIN / IS NULL
esANTI JOIN
durch seinen Optimierer nicht zu einem optimiert werden kann.In
PostgreSQL
,LEFT JOIN / IS NULL
undNOT EXISTS
sind effizienter alsNOT IN
sine sie auf eine optimierteAnti Join
, währendNOT IN
Anwendungenhashed subplan
(oder auch eine einfache ,subplan
wenn die Unterabfrage zu groß , um Hash ist)quelle
NOT EXISTS
als TRUE ausgewertet, wenn die darin enthaltene Abfrage Zeilen zurückgibt.SELECT NULL
könnte auchSELECT *
oderSELECT 1
oder irgendetwas anderes sein, dasNOT EXISTS
Prädikat betrachtet die Werte der Zeilen nicht, sondern zählt sie nur.Wenn Sie möchten, dass die Welt ein zweiwertiger boolescher Ort ist, müssen Sie den Nullfall (dritter Wert) selbst verhindern.
Schreiben Sie keine IN-Klauseln, die Nullen auf der Listenseite zulassen. Filtern Sie sie heraus!
quelle
common_id not in
können wir nochcommon_id
Wert haben, der istNULL
. Besteht das Problem, keine Ergebnisse zu erzielen, nicht weiterhin?Tabelle1 oder Tabelle2 enthält einige Nullwerte für common_id. Verwenden Sie stattdessen diese Abfrage:
quelle
quelle
Ganz oben auf meinem Kopf ...
Ich habe einige Tests durchgeführt und hier waren meine Ergebnisse für die Antwort von @ patmortech und die Kommentare von @ rexem.
Wenn entweder Tabelle1 oder Tabelle2 nicht auf commonID indiziert ist, erhalten Sie einen Tabellenscan, aber die Abfrage von @ patmortech ist immer noch doppelt so schnell (für eine 100K-Zeilen-Mastertabelle).
Wenn beide nicht auf commonID indiziert sind, erhalten Sie zwei Tabellenscans und der Unterschied ist vernachlässigbar.
Wenn beide auf commonID indiziert sind, wird die Abfrage "nicht vorhanden" in 1/3 der Zeit ausgeführt.
quelle
quelle
Nehmen wir diese Werte für common_id an:
Wir möchten, dass die Zeile in Common zurückgegeben wird, da sie in keiner der anderen Tabellen vorhanden ist. Die Null wirft jedoch einen Schraubenschlüssel ein.
Mit diesen Werten entspricht die Abfrage:
Das entspricht:
Hier beginnt das Problem. Beim Vergleich mit einer Null ist die Antwort unbekannt . Die Abfrage reduziert sich also auf
falsch oder unbekannt ist unbekannt:
wahr und nicht unbekannt ist auch unbekannt:
Die where-Bedingung gibt keine Datensätze zurück, bei denen das Ergebnis unbekannt ist, sodass wir keine Datensätze zurückerhalten.
Eine Möglichkeit, damit umzugehen, besteht darin, den vorhandenen Operator anstelle von in zu verwenden. Exists gibt niemals unbekannt zurück, da er eher mit Zeilen als mit Spalten arbeitet. (Eine Zeile existiert entweder oder nicht; keine dieser Null-Mehrdeutigkeiten auf Zeilenebene!)
quelle
das hat bei mir funktioniert :)
quelle
NOT IN
auftreten?quelle
Ich hatte ein Beispiel, in dem ich nachgeschlagen habe, und da eine Tabelle den Wert als Double und die andere als Zeichenfolge enthielt, stimmten sie nicht überein (oder stimmten nicht ohne Besetzung überein). Aber nur NICHT IN . Als SELECT ... IN ... funktionierte. Seltsam, aber ich dachte, ich würde es teilen, falls jemand anderes auf diese einfache Lösung stößt.
quelle
Bitte folgen Sie dem folgenden Beispiel, um das obige Thema zu verstehen:
Sie können auch den folgenden Link besuchen, um Anti-Join zu erfahren
Aber wenn wir
NOT IN
in diesem Fall verwenden, erhalten wir keine Daten.Dies geschieht, wenn (
select department_id from hr.employees
) einen Nullwert zurückgibt und die gesamte Abfrage als falsch ausgewertet wird. Wir können es sehen, wenn wir die SQL wie unten leicht ändern und Nullwerte mit der NVL-Funktion behandeln.Jetzt bekommen wir Daten:
Wieder erhalten wir Daten, da wir den Nullwert mit der NVL-Funktion behandelt haben.
quelle