Welche dieser Abfragen ist die schnellere?
EXISTIERT NICHT:
SELECT ProductID, ProductName
FROM Northwind..Products p
WHERE NOT EXISTS (
SELECT 1
FROM Northwind..[Order Details] od
WHERE p.ProductId = od.ProductId)
Oder NICHT IN:
SELECT ProductID, ProductName
FROM Northwind..Products p
WHERE p.ProductID NOT IN (
SELECT ProductID
FROM Northwind..[Order Details])
Der Ausführungsplan für Abfragen besagt, dass beide dasselbe tun. Wenn dies der Fall ist, welches ist das empfohlene Formular?
Dies basiert auf der NorthWind-Datenbank.
[Bearbeiten]
Ich habe gerade diesen hilfreichen Artikel gefunden: http://weblogs.sqlteam.com/mladenp/archive/2007/05/18/60210.aspx
Ich denke, ich bleibe bei NOT EXISTS.
sql
sql-server
notin
ilitirit
quelle
quelle
NOT IN
Abfrage:SELECT "A".* FROM "A" WHERE "A"."id" NOT IN (SELECT "B"."Aid" FROM "B" WHERE "B"."Uid" = 2)
fast 30-mal so schnell wie dieseNOT EXISTS
:SELECT "A".* FROM "A" WHERE (NOT (EXISTS (SELECT 1 FROM "B" WHERE "B"."user_id" = 2 AND "B"."Aid" = "A"."id")))
Antworten:
Ich benutze immer standardmäßig
NOT EXISTS
.Die Ausführungspläne können gleich im Moment, aber wenn entweder Spalte wird in Zukunft geändert , damit
NULL
s dieNOT IN
Version benötigt mehr Arbeit zu tun (auch wenn keineNULL
s in den Daten tatsächlich vorhanden sind) und die Semantik ,NOT IN
wennNULL
s sind vorhanden Es ist unwahrscheinlich, dass es die sind, die Sie sowieso wollen.Wenn weder
Products.ProductID
oder[Order Details].ProductID
erlaubenNULL
s dasNOT IN
wird identisch mit der folgenden Abfrage behandelt.Der genaue Plan kann variieren, aber für meine Beispieldaten erhalte ich Folgendes.
Ein ziemlich häufiges Missverständnis scheint zu sein, dass korrelierte Unterabfragen im Vergleich zu Joins immer "schlecht" sind. Sie können es sicherlich sein, wenn sie einen Plan für verschachtelte Schleifen erzwingen (Unterabfrage wird zeilenweise ausgewertet), aber dieser Plan enthält einen logischen Anti-Semi-Join-Operator. Anti-Semi-Joins sind nicht auf verschachtelte Schleifen beschränkt, sondern können auch Hash- oder Merge-Joins (wie in diesem Beispiel) verwenden.
Wenn
[Order Details].ProductID
istNULL
-able die Abfrage dann wirdDer Grund dafür ist, dass die korrekte Semantik, wenn sie s
[Order Details]
enthält, darinNULL
ProductId
besteht, keine Ergebnisse zurückzugeben. Überprüfen Sie die zusätzliche Spule für Anti-Semi-Join und Zeilenanzahl, um zu überprüfen, ob diese dem Plan hinzugefügt wurde.Wenn
Products.ProductID
auch geändert wird, umNULL
-able zu werden, wird die Abfrage dannDer Grund dafür ist, dass a
NULL
Products.ProductId
in den Ergebnissen nicht zurückgegeben werden sollte, es sei denn, dieNOT IN
Unterabfrage würde überhaupt keine Ergebnisse zurückgeben (dh die[Order Details]
Tabelle ist leer). In welchem Fall sollte es. Im Plan für meine Beispieldaten wird dies durch Hinzufügen eines weiteren Anti-Semi-Joins wie unten implementiert.Der Effekt davon wird in dem Blog-Beitrag gezeigt, der bereits von Buckley verlinkt wurde . Im dortigen Beispiel steigt die Anzahl der logischen Lesevorgänge von rund 400 auf 500.000.
Zusätzlich macht die Tatsache, dass eine einzelne
NULL
die Zeilenanzahl auf Null reduzieren kann, die Kardinalitätsschätzung sehr schwierig. Wenn SQL Server davon ausgeht, dass dies passieren wird, aber tatsächlich keineNULL
Zeilen in den Daten vorhanden sind, kann der Rest des Ausführungsplans katastrophal schlechter sein, wenn dies nur Teil einer größeren Abfrage ist und unangemessene verschachtelte Schleifen die wiederholte Ausführung eines teuren Unterprogramms verursachen Baum zum Beispiel .Dies ist jedoch nicht der einzig mögliche Ausführungsplan für eine Spalte
NOT IN
mit einemNULL
Wert. Dieser Artikel zeigt einen anderen für eine Abfrage für dieAdventureWorks2008
Datenbank.Für die Spalte
NOT IN
auf einerNOT NULL
Spalte oderNOT EXISTS
gegen eine nullbare oder nicht nullbare Spalte wird der folgende Plan angegeben.Wenn die Spalte in
NULL
-able geändert wird, sieht derNOT IN
Plan jetzt so ausEs fügt dem Plan einen zusätzlichen inneren Verknüpfungsoperator hinzu. Diese Vorrichtung wird hier erklärt . Es ist alles vorhanden, um die vorherige Suche mit einem einzelnen korrelierten Index
Sales.SalesOrderDetail.ProductID = <correlated_product_id>
in zwei Suchvorgänge pro äußerer Zeile umzuwandeln . Der zusätzliche ist eingeschaltetWHERE Sales.SalesOrderDetail.ProductID IS NULL
.Da dies unter einem Anti-Semi-Join liegt, wird die zweite Suche nicht durchgeführt, wenn dieser Zeilen zurückgibt. Wenn
Sales.SalesOrderDetail
jedoch keineNULL
ProductID
s enthalten sind , wird die Anzahl der erforderlichen Suchvorgänge verdoppelt.quelle
NOT EXISTS
funktioniert, wie ich es erwartetNOT IN
habe (was es nicht tut).Beachten Sie auch, dass NOT IN nicht gleich NOT EXISTS ist, wenn es um Null geht.
Dieser Beitrag erklärt es sehr gut
http://sqlinthewild.co.za/index.php/2010/02/18/not-exists-vs-not-in/
quelle
Wenn der Ausführungsplaner sagt, dass sie gleich sind, sind sie gleich. Verwenden Sie diejenige, die Ihre Absicht deutlicher macht - in diesem Fall die zweite.
quelle
Eigentlich glaube ich, dass dies das schnellste wäre:
quelle
Ich habe eine Tabelle mit ungefähr 120.000 Datensätzen und muss nur diejenigen auswählen, die in vier anderen Tabellen mit einer Anzahl von Zeilen von ca. 1500, 4000, 40000, 200 nicht vorhanden sind (mit einer Varchar-Spalte abgeglichen). Alle beteiligten Tabellen haben einen eindeutigen Index auf die betroffenen
Varchar
Spalte.NOT IN
dauerte etwa 10 Minuten,NOT EXISTS
dauerte 4 Sekunden.Ich habe eine rekursive Abfrage, die möglicherweise einen nicht abgestimmten Abschnitt hatte, der möglicherweise zu den 10 Minuten beigetragen hat, aber die andere Option, die 4 Sekunden dauert, erklärt, zumindest für mich, dass dies
NOT EXISTS
weitaus besser ist oder zumindest dasIN
undEXISTS
nicht genau das gleiche und immer einen Wert hat Überprüfen Sie dies, bevor Sie mit dem Code fortfahren.quelle
In Ihrem speziellen Beispiel sind sie gleich, da der Optimierer herausgefunden hat, dass das, was Sie versuchen, in beiden Beispielen gleich ist. Es ist jedoch möglich, dass der Optimierer dies in nicht trivialen Beispielen nicht tut, und in diesem Fall gibt es Gründe, gelegentlich einen anderen vorzuziehen.
NOT IN
sollte bevorzugt werden, wenn Sie mehrere Zeilen in Ihrer äußeren Auswahl testen. Die Unterabfrage in derNOT IN
Anweisung kann zu Beginn der Ausführung ausgewertet werden, und die temporäre Tabelle kann mit jedem Wert in der äußeren Auswahl verglichen werden, anstatt die Unterauswahl jedes Mal erneut auszuführen, wie dies für dieNOT EXISTS
Anweisung erforderlich wäre .Wenn die Unterabfrage mit der äußeren Auswahl korreliert werden muss , ist dies
NOT EXISTS
möglicherweise vorzuziehen, da der Optimierer möglicherweise eine Vereinfachung feststellt, die die Erstellung temporärer Tabellen verhindert, um dieselbe Funktion auszuführen.quelle
Ich habe benutzt
und stellte fest, dass es falsche Ergebnisse gab (mit falsch meine ich keine Ergebnisse). Da gab es eine NULL in TABLE2.Col1.
Beim Ändern der Abfrage in
gab mir die richtigen Ergebnisse.
Seitdem benutze ich NOT EXISTS überall.
quelle
Sie sind sehr ähnlich, aber nicht wirklich gleich.
In Bezug auf die Effizienz habe ich festgestellt, dass der linke Join eine Null- Anweisung ist, die effizienter ist (wenn also eine Fülle von Zeilen ausgewählt werden soll).
quelle
Wenn der Optimierer angibt, dass sie gleich sind, berücksichtigen Sie den menschlichen Faktor. Ich sehe lieber NICHT EXISTS :)
quelle
Datenbanktabellenmodell
Nehmen wir an, wir haben die folgenden zwei Tabellen in unserer Datenbank, die eine Eins-zu-Viele-Tabellenbeziehung bilden.
Die
student
Tabelle ist die übergeordnete und diestudent_grade
untergeordnete Tabelle, da sie eine Fremdschlüsselspalte student_id enthält, die auf die ID-Primärschlüsselspalte in der Schülertabelle verweist.Das
student table
enthält die folgenden zwei Datensätze:In der
student_grade
Tabelle sind die Noten aufgeführt, die die Schüler erhalten haben:SQL EXISTIERT
Nehmen wir an, wir möchten alle Schüler erreichen, die im Mathematikunterricht eine Note von 10 erhalten haben.
Wenn wir nur an der Studentenkennung interessiert sind, können wir eine Abfrage wie diese ausführen:
Die Anwendung ist jedoch daran interessiert, den vollständigen Namen von a anzuzeigen
student
, nicht nur den Bezeichner. Daher benötigen wir auch Informationen aus derstudent
Tabelle.Um die
student
Datensätze mit einer Note von 10 in Mathematik zu filtern , können Sie den EXISTS SQL-Operator wie folgt verwenden:Wenn Sie die obige Abfrage ausführen, sehen Sie, dass nur die Alice-Zeile ausgewählt ist:
Die äußere Abfrage wählt die
student
Zeilenspalten aus, die an den Client zurückgegeben werden sollen. Die WHERE-Klausel verwendet jedoch den EXISTS-Operator mit einer zugehörigen inneren Unterabfrage.Der EXISTS-Operator gibt true zurück, wenn die Unterabfrage mindestens einen Datensatz zurückgibt, und false, wenn keine Zeile ausgewählt ist. Das Datenbankmodul muss die Unterabfrage nicht vollständig ausführen. Wenn ein einzelner Datensatz übereinstimmt, gibt der EXISTS-Operator true zurück und die zugehörige andere Abfragezeile wird ausgewählt.
Die innere Unterabfrage ist korreliert, da die Spalte student_id der
student_grade
Tabelle mit der Spalte id der äußeren Schülertabelle abgeglichen wird.SQL existiert nicht
Angenommen, wir möchten alle Schüler auswählen, deren Note nicht niedriger als 9 ist. Dazu können wir NOT EXISTS verwenden, wodurch die Logik des EXISTS-Operators negiert wird.
Daher gibt der Operator NOT EXISTS true zurück, wenn die zugrunde liegende Unterabfrage keinen Datensatz zurückgibt. Wenn jedoch ein einzelner Datensatz mit der inneren Unterabfrage übereinstimmt, gibt der Operator NOT EXISTS false zurück und die Ausführung der Unterabfrage kann gestoppt werden.
Um alle Schülerdatensätze, denen kein student_grade zugeordnet ist, mit einem Wert unter 9 abzugleichen, können Sie die folgende SQL-Abfrage ausführen:
Wenn Sie die obige Abfrage ausführen, sehen Sie, dass nur der Alice-Datensatz übereinstimmt:
Der Vorteil der Verwendung der SQL EXISTS- und NOT EXISTS-Operatoren besteht also darin, dass die Ausführung der inneren Unterabfrage gestoppt werden kann, solange ein übereinstimmender Datensatz gefunden wird.
quelle
Es hängt davon ab, ob..
wäre nicht relativ langsam, das ist nicht viel, um die Größe der Abfrage zu begrenzen, um zu sehen, ob sie eingegeben werden. EXISTS wäre in diesem Fall vorzuziehen.
Abhängig vom Optimierer des DBMS kann dies jedoch nicht anders sein.
Als Beispiel dafür, wann EXISTS besser ist
quelle
IN
undEXISTS
erhalten Sie den gleichen Plan in SQL Server . Die Frage ist sowiesoNOT IN
gegen vs.NOT EXISTS