Sollte NICHT IN vermieden werden?

14

Unter einigen SQL Server-Entwicklern ist es eine weit verbreitete Überzeugung, dass NOT INes furchtbar langsam ist , und Abfragen sollten umgeschrieben werden, damit sie dasselbe Ergebnis zurückgeben, aber nicht die "bösen" Schlüsselwörter verwenden. ( Beispiel ).

Ist das wahr?

Gibt es zum Beispiel einen bekannten Fehler in SQL Server (welche Version?), Der dazu führt, dass Abfragen mit NOT INeinem schlechteren Ausführungsplan ausgeführt werden als eine entsprechende Abfrage, die verwendet wird

  • ein LEFT JOINmit einem kombinierten NULLScheck oder
  • (SELECT COUNT(*) ...) = 0in der WHEREKlausel?
Heinzi
quelle
7
Dieser Artikel ist jedoch sehr ungenau. "In" muss nicht "für jede Zeile in TableOne immer wieder dieselbe Abfrage ausführen". Das Plakat dort scheint zu glauben, dass IN/ NOT INimmer mit verschachtelten Schleifen implementiert wird. Und ich habe keine Ahnung, was stops SQL Server from creating a ‘plan’das bedeuten soll.
Martin Smith
5
@Heinzi Der Artikel, auf den du verlinkst, sollte im Feuer sterben, er steckt voller Unsinn. Zum Beispiel : "Um IN zu ersetzen, verwenden wir einen INNER JOIN. Sie sind praktisch dasselbe." Das Problem ist, dass sie nicht dasselbe sind. Ich würde niemandem vertrauen, der kein grundlegendes SQL kennt, dh den Unterschied zwischen einem Join und einem Semi-Join, um etwas über das Verhalten von SQL-Server zu analysieren.
Ypercubeᵀᴹ

Antworten:

14

Ich glaube nicht, dass es etwas damit zu tun hat, schrecklich langsam zu sein. es hat damit zu tun, dass es möglicherweise ungenau ist. Zum Beispiel mit den folgenden Daten - Bestellungen, die entweder von einem einzelnen Kunden oder einem B2B-Partner aufgegeben werden könnten:

DECLARE @Customers TABLE(CustomerID INT);

INSERT @Customers VALUES(1),(2);

DECLARE @Orders TABLE(OrderID INT, CustomerID INT, CompanyID INT);

INSERT @Orders VALUES(10,1,NULL),(11,NULL,5);

Angenommen, ich möchte alle Kunden finden, die noch nie eine Bestellung aufgegeben haben. In Anbetracht der Daten gibt es nur einen: Kunde 2. Es gibt drei Möglichkeiten, wie ich eine Abfrage schreiben kann, um diese Informationen zu finden (es gibt andere):

SELECT [NOT IN] = CustomerID FROM @Customers 
  WHERE CustomerID NOT IN (SELECT CustomerID FROM @Orders);

SELECT [NOT EXISTS] = CustomerID FROM @Customers AS c 
  WHERE NOT EXISTS (SELECT 1 FROM @Orders AS o
  WHERE o.CustomerID = c.CustomerID);

SELECT [EXCEPT] = CustomerID FROM @Customers
EXCEPT SELECT CustomerID FROM @Orders;

Ergebnisse:

NOT IN
------
                 -- <-- no results. Is that what you expected?

NOT EXISTS
----------
2

EXCEPT
------
2

Nun gibt es auch einige Leistungsprobleme, über die ich in diesem Blogeintrag spreche . Abhängig von den Daten und Indizes NOT EXISTSwird in der Regel eine Outperformance NOT INerzielt, und ich weiß nicht, ob sich die Leistung jemals verschlechtern könnte. Sie sollten auch beachten, dass EXCEPTeine bestimmte Sortieroperation eingeführt werden kann, sodass Sie möglicherweise unterschiedliche Daten erhalten (wiederum abhängig von der Quelle). Und dass das beliebte LEFT OUTER JOIN ... WHERE right.column IS NULLMuster immer das schlechteste ist.

Martin Smith hat auch in seiner Antwort auf SO eine Menge guter Hintergrundinformationen .

Aaron Bertrand
quelle