SQL Server IN vs. EXISTS-Leistung

115

Ich bin gespannt, welche der folgenden Möglichkeiten effizienter ist.

Ich war immer etwas vorsichtig bei der Verwendung, INweil ich glaube, dass SQL Server die Ergebnismenge in eine große IFAussage verwandelt . Bei einer großen Ergebnismenge kann dies zu einer schlechten Leistung führen. Bei kleinen Ergebnismengen bin ich mir nicht sicher, ob dies auch vorzuziehen ist. Wäre es für große Ergebnismengen nicht EXISTSeffizienter?

WHERE EXISTS (SELECT * FROM Base WHERE bx.BoxID = Base.BoxID AND [Rank] = 2)

vs.

WHERE bx.BoxID IN (SELECT BoxID FROM Base WHERE [Rank = 2])
Randy Minder
quelle
8
Der beste Weg, dies herauszufinden, besteht darin, es auszuprobieren und einige Messungen durchzuführen.
Klaus Byskov Pedersen
10
es gibt bekommt eine Unmenge Duplikate für diese sein ......
marc_s
5
@marc_s - Wahrscheinlich, aber in der Zeit, in der ich alle Beiträge zu diesem Thema durchgesehen und einen gefunden hätte, der zu meinem Fall passt, hatte ich vier Antworten auf meine Frage.
Randy Minder
7
Zu Ihrer Information , wenn Sie das wollen , sind die meisten performante Art und Weise, können Sie select 1 from Base...in Ihrem where existsda Sie eigentlich gar nicht über die Ergebnisse kümmern, nur , dass eine Reihe tatsächlich existiert.
Brad
2
@marc_s das ist wirklich traurig, weil ich mir die Zeit genommen habe, die Beiträge durchzusehen, um dem Stackoverflow keinen Müll mehr hinzuzufügen. Ich brauche keine maßgeschneiderte Antwort, um meine Arbeit zu erledigen. Das ist die Art des Denkens, die ein Gazillion-Duplikat anstelle von nur wenigen mit guten Antworten
hinzufügte

Antworten:

140

EXISTS wird schneller sein, denn sobald der Motor einen Treffer gefunden hat, hört er auf zu schauen, da sich der Zustand als wahr erwiesen hat.

Mit INwerden alle Ergebnisse der Unterabfrage vor der weiteren Verarbeitung erfasst.

keithwarren7
quelle
4
Das ist ein guter Punkt. Für die IN-Anweisung muss SQL Server eine vollständige Ergebnismenge generieren und dann eine große IF-Anweisung erstellen, denke ich.
Randy Minder
72
Dies war früher der Fall, aber in aktuellen Versionen (mindestens 2008) ist der Optimierer viel intelligenter ... er behandelt IN () genau wie EXISTS ().
Aaron Bertrand
11
@ Aaron - ja, normalerweise erstellt der Optimierer intern einen besseren Plan. In komplexeren Szenarien kann es jedoch nachteilig sein, sich auf interne Verknüpfungen zu verlassen.
Scott Coates
2
Das ist einfach falsch. Es war im Jahr 2010 und ist es immer noch.
Magnus
2
IN und EXISTS haben genau den gleichen Abfrageplan und IO. Es gibt keinen Grund zu der Annahme, dass ihre Leistung unterschiedlich ist. Überprüfen Sie Ihre Zeitstatistik und erstellen Sie sich selbst
Nelssen
40

Die akzeptierte Antwort ist kurzsichtig und die Frage etwas locker:

1) Erwähnen Sie weder explizit, ob links, rechts oder auf beiden Seiten ein Deckungsindex vorhanden ist.

2) Beides berücksichtigt nicht die Größe des Eingangs für die linke Seite und des Eingangs für die rechte Seite.
(Die Frage erwähnt nur eine insgesamt große Ergebnismenge ).

Ich glaube, der Optimierer ist intelligent genug, um zwischen "in" und "existiert" zu konvertieren, wenn aufgrund von (1) und (2) ein erheblicher Kostenunterschied besteht, andernfalls kann er nur als Hinweis verwendet werden (z. B. existiert, um die Verwendung von zu fördern) ein suchbarer Index auf der rechten Seite).

Beide Formulare können intern in Verknüpfungsformulare konvertiert, die Verknüpfungsreihenfolge umgekehrt und als Schleife, Hash oder Zusammenführung ausgeführt werden - basierend auf den geschätzten Zeilenzahlen (links und rechts) und der Indexexistenz auf der linken, rechten oder beiden Seiten.

crokusek
quelle
3
Ich weiß nicht, warum diese ausgezeichnete Antwort keine Aufmerksamkeit mehr erhalten hat. Das Verständnis des Index / der Struktur für beide Seiten könnte Auswirkungen haben, da stimme ich zu. Gut gesagt.
SheldonH
Der Optimierer gibt immer den gleichen Plan für INund EXISTS. Versuchen Sie, einen Fall zu finden, in dem sie nicht den gleichen Plan erhalten (obwohl dies nicht für NOT INund gilt NOT EXISTS)
Martin Smith
@MartinSmith Ich nehme an, Sie wissen, wovon Sie sprechen, aber haben Sie einen Beweis dafür, dass die Pläne immer gleich sind? Wenn ja, würde dies die jahrzehntelange Meinungsverschiedenheit hier aufklären.
MarredCheese
@MarredCheese - die Verantwortung liegt bei den Leuten, die behaupten, dass es anders ist, ein einziges Beispiel dafür zu produzieren
Martin Smith
37

Ich habe einige Tests mit SQL Server 2005 und 2008 durchgeführt, und sowohl auf EXISTS als auch auf IN wird genau derselbe tatsächliche Ausführungsplan verwendet, wie andere angegeben haben. Der Optimierer ist optimal. :) :)

Beachten Sie jedoch, dass EXISTS, IN und JOIN manchmal unterschiedliche Ergebnisse zurückgeben können, wenn Sie Ihre Abfrage nicht richtig formulieren: http://weblogs.sqlteam.com/mladenp/archive/2007/05/18/60210 .aspx

Adam Nofsinger
quelle
5

Hier gibt es viele irreführende Antworten, einschließlich der hoch bewerteten Antworten (obwohl ich nicht glaube, dass ihre Operationen Schaden anrichteten). Die kurze Antwort lautet: Diese sind gleich.

Es gibt viele Schlüsselwörter in der (T-) SQL-Sprache, aber am Ende passieren auf der Hardware nur die Vorgänge, die im Ausführungsabfrageplan aufgeführt sind.

Die relationale (Mathematik Theorie) Operation , die wir tun , wenn wir berufen [NOT] INund [NOT] EXISTSist die halb join (anti-beitreten bei der Verwendung NOT). Es ist kein Zufall, dass die entsprechenden SQL Server-Vorgänge denselben Namen haben . Es gibt keine Operation, die erwähnt INoder EXISTSirgendwo - nur (Anti-) Semi-Joins. Somit gibt es keine Möglichkeit , dass ein logisch-äquivalent INvs EXISTSWahl Leistung beeinträchtigen könnte , weil es eine gibt und einzigen Weg, die (anti) halbAusführungsOperation teilnehmen, um ihre Ergebnisse zu erhalten .

Ein Beispiel:

Abfrage 1 ( Plan )

select * from dt where dt.customer in (select c.code from customer c where c.active=0)

Abfrage 2 ( Plan )

select * from dt where exists (select 1 from customer c where c.code=dt.customer and c.active=0)
George Menoutis
quelle
Hast du es getestet? Wenn ja, können Sie Ihre SQL und Ihre Ergebnisse teilen?
UnhandledExcepSean
Mehrmals getestet. Ich kann einen anderen Testfall erstellen, und das werde ich auch, aber ein Testfall bedeutet nicht, dass der Optimierer genau denselben Plan für Tabellen mit unterschiedlichen Statistiken ausführt. Dies könnte dazu führen, dass jemand denkt, die Antwort sei teilweise - aber das Nichtvorhandensein mehrerer Semijoin-Operatoren ist eine Tatsache. Vielleicht finde ich irgendwo eine Liste und verlinke sie.
George Menoutis
5

Ich würde mit EXISTS über IN gehen, siehe untenstehenden Link:

SQL Server: JOIN vs IN vs EXISTS - der logische Unterschied

Es gibt ein weit verbreitetes Missverständnis, dass sich IN in Bezug auf die zurückgegebenen Ergebnisse genauso wie EXISTS oder JOIN verhält. Das ist einfach nicht wahr.

IN: Gibt true zurück, wenn ein angegebener Wert mit einem Wert in einer Unterabfrage oder einer Liste übereinstimmt.

Exists: Gibt true zurück, wenn eine Unterabfrage Zeilen enthält.

Join: Verbindet 2 Ergebnismengen in der Join-Spalte.

Blog-Gutschrift: https://stackoverflow.com/users/31345/mladen-prajdic

Gerber
quelle
Wow, danke für deinen Blog und deine Erklärung.
Christian Müller
3

Die Ausführungspläne sind in diesen Fällen normalerweise identisch, aber bis Sie sehen, wie der Optimierer alle anderen Aspekte von Indizes usw. berücksichtigt, werden Sie es wirklich nie erfahren.

Cade Roux
quelle
3

IN ist also nicht dasselbe wie EXISTS und erzeugt auch nicht denselben Ausführungsplan.

Normalerweise wird EXISTS in einer korrelierten Unterabfrage verwendet. Dies bedeutet, dass Sie die innere EXISTS-Abfrage mit Ihrer äußeren Abfrage verbinden. Dadurch werden weitere Schritte hinzugefügt, um ein Ergebnis zu erzielen, da Sie die äußeren Abfrageverknüpfungen und die inneren Abfrageverknüpfungen lösen müssen und dann mit den where-Klauseln übereinstimmen, um beide zu verbinden.

Normalerweise wird IN verwendet, ohne die innere Abfrage mit der äußeren Abfrage zu korrelieren, und dies kann in nur einem Schritt gelöst werden (im besten Fall).

Bedenken Sie:

  1. Wenn Sie IN verwenden und das Ergebnis der inneren Abfrage Millionen von Zeilen mit unterschiedlichen Werten enthält, wird es wahrscheinlich langsamer als EXISTS ausgeführt, da die EXISTS-Abfrage performant ist (über die richtigen Indizes verfügt, um mit der äußeren Abfrage zu verknüpfen).

  2. Wenn Sie EXISTS verwenden und der Join mit Ihrer äußeren Abfrage komplex ist (die Ausführung dauert länger, keine geeigneten Indizes), wird die Abfrage um die Anzahl der Zeilen in der äußeren Tabelle verlangsamt. Manchmal kann die geschätzte Zeit bis zum Abschluss in Tagen liegen. Wenn die Anzahl der Zeilen für die angegebene Hardware akzeptabel ist oder die Kardinalität der Daten korrekt ist (z. B. weniger DISTINCT-Werte in einem großen Datensatz), kann IN schneller als EXISTS ausgeführt werden.

  3. All dies wird beachtet, wenn Sie eine angemessene Anzahl von Zeilen in jeder Tabelle haben (mit fair meine ich etwas, das Ihre CPU-Verarbeitung und / oder RAM-Schwellenwerte für das Caching überschreitet).

Die ANTWORT ist also ABHÄNGIG. Sie können eine komplexe Abfrage in IN oder EXISTS schreiben. Als Faustregel sollten Sie jedoch versuchen, IN mit einer begrenzten Anzahl unterschiedlicher Werte und EXISTS zu verwenden, wenn Sie viele Zeilen mit vielen unterschiedlichen Werten haben.

Der Trick besteht darin, die Anzahl der zu scannenden Zeilen zu begrenzen.

Grüße,

MarianoC

MarianoC
quelle
1

Um das zu optimieren EXISTS, sei sehr wörtlich; Es muss nur etwas vorhanden sein, aber Sie benötigen keine Daten, die von der korrelierten Unterabfrage zurückgegeben werden. Sie bewerten nur eine boolesche Bedingung.

So:

WHERE EXISTS (SELECT TOP 1 1 FROM Base WHERE bx.BoxID = Base.BoxID AND [Rank] = 2)

Da die korrelierte Unterabfrage lautet RBAR, erfüllt der erste Ergebnistreffer die Bedingung und wird nicht weiter verarbeitet.

Josh Lewis
quelle
Ich würde bei der Verwendung von LEFT JOIN + NULL-Codierung immer äußerst vorsichtig sein, da es sehr leicht ist, verpasste oder verzerrte Ergebnisse zu erhalten, wenn Sie bei der Behandlung von NULL nicht sehr vorsichtig sind. Ich habe sehr selten eine Situation gefunden, in der EXISTS oder ein CTE (zum Auffinden von Duplikaten oder synthetischen Einfügungen für fehlende Daten) nicht beide die gleichen Anforderungen erfüllen und den LEFT JOIN + NULL
Josh Lewis
3
TOP 1 sollte bei Verwendung mit EXISTS vollständig fremd (oder ereignisredundant) sein. EXISTS kehrt immer zurück, sobald eine passende Zeile gefunden wurde.
Karl Kieninger
Bei diesem Ansatz habe ich bisher keinen Leistungsvorteil gesehen. Bitte zeigen Sie einige Screenshots der Ausführungspläne
DaFi4
-1

Aus dem Kopf und nicht garantiert richtig: Ich glaube, der zweite wird in diesem Fall schneller sein.

  1. In der ersten wird die korrelierte Unterabfrage wahrscheinlich dazu führen, dass die Unterabfrage für jede Zeile ausgeführt wird.
  2. Im zweiten Beispiel sollte die Unterabfrage nur einmal ausgeführt werden, da sie nicht korreliert ist.
  3. Im zweiten Beispiel wird der INWille kurzgeschlossen, sobald eine Übereinstimmung gefunden wird.
RedFilter
quelle