Best Practice zwischen der Verwendung von LEFT JOIN oder NOT EXISTS

67

Gibt es eine bewährte Methode zwischen der Verwendung eines LEFT JOIN- oder eines NOT EXISTS-Formats?

Was bringt es, wenn man eins über das andere setzt?

Wenn keine, welche ist vorzuziehen?

SELECT *
FROM tableA A
LEFT JOIN tableB B
     ON A.idx = B.idx
WHERE B.idx IS NULL

SELECT *
FROM tableA A
WHERE NOT EXISTS
(SELECT idx FROM tableB B WHERE B.idx = A.idx)

Ich verwende Abfragen in Access für eine SQL Server-Datenbank.

Michael Richardson
quelle
2
Als beiseite, die scheinbar identischen Ansatz WHERE A.idx NOT IN (...) ist nicht identisch aufgrund des dreiwertigen Verhalten NULL(dh NULLnicht gleich ist NULL(noch ungleich), also , wenn Sie irgendwelche NULL in tableBunerwartete Ergebnisse erhalten!)
Elaskanator

Antworten:

58

Der größte Unterschied besteht nicht in der Verbindung vs nicht existiert, es ist (wie geschrieben), die SELECT *.

Im ersten Beispiel erhalten Sie alle Spalten von beiden A und Bim zweiten Beispiel nur Spalten von A.

In SQL Server ist die zweite Variante in einem sehr einfachen konstruierten Beispiel etwas schneller:

Erstellen Sie zwei Beispieltabellen:

CREATE TABLE dbo.A
(
    A_ID INT NOT NULL
        PRIMARY KEY CLUSTERED
        IDENTITY(1,1)
);

CREATE TABLE dbo.B
(
    B_ID INT NOT NULL
        PRIMARY KEY CLUSTERED
        IDENTITY(1,1)
);
GO

Fügen Sie 10.000 Zeilen in jede Tabelle ein:

INSERT INTO dbo.A DEFAULT VALUES;
GO 10000

INSERT INTO dbo.B DEFAULT VALUES;
GO 10000

Entfernen Sie jede fünfte Zeile aus der zweiten Tabelle:

DELETE 
FROM dbo.B 
WHERE B_ID % 5 = 1;

SELECT COUNT(*) -- shows 10,000
FROM dbo.A;

SELECT COUNT(*) -- shows  8,000
FROM dbo.B;

Führen Sie die beiden SELECTTestanweisungsvarianten durch:

SELECT *
FROM dbo.A
    LEFT JOIN dbo.B ON A.A_ID = B.B_ID
WHERE B.B_ID IS NULL;

SELECT *
FROM dbo.A
WHERE NOT EXISTS (SELECT 1
    FROM dbo.B
    WHERE b.B_ID = a.A_ID);

Ausführungspläne:

Bildbeschreibung hier eingeben

Die zweite Variante muss die Filteroperation nicht ausführen, da sie den linken Antisemi- Join-Operator verwenden kann.

Max Vernon
quelle
23

Sie sind logischerweise identisch, liegen jedoch NOT EXISTSnäher an dem von Ihnen gewünschten AntiSemiJoin und werden im Allgemeinen bevorzugt. Es wird auch besser hervorgehoben, dass Sie nicht auf die Spalten in B zugreifen können, da sie nur als Filter verwendet werden (im Gegensatz dazu, dass sie mit NULL-Werten verfügbar sind).

Vor vielen Jahren (SQL Server 6.0 ish) LEFT JOINwar es schneller, aber das war lange nicht mehr der Fall. In diesen Tagen NOT EXISTSist es etwas schneller.


Die größte Auswirkung in Access besteht darin, dass die JOINMethode den Join abschließen muss, bevor er gefiltert wird, um den verknüpften Satz im Speicher zu erstellen. Wenn Sie NOT EXISTSes verwenden, wird nach der Zeile gesucht, den Spalten wird jedoch kein Speicherplatz zugewiesen. Außerdem hört es auf zu suchen, sobald es eine Zeile findet. Die Leistung in Access variiert etwas stärker, aber eine allgemeine Faustregel lautet, dass die Leistung NOT EXISTStendenziell etwas schneller ist. Ich würde weniger geneigt sein zu sagen, dass es sich um "Best Practice" handelt, da es sich um mehr Faktoren handelt.

Rob Farley
quelle
6

Eine Ausnahme, die mir aufgefallen ist NOT EXISTS, LEFT JOIN ... WHERE IS NULList die Verwendung von Verbindungsservern .

Aus der Prüfung der Ausführungspläne geht hervor, dass der NOT EXISTSOperator in einer verschachtelten Schleife ausgeführt wird. Wobei es zeilenweise ausgeführt wird (was ich für sinnvoll halte).

Beispielausführungsplan, der dieses Verhalten demonstriert: Bildbeschreibung hier eingeben

Robopim
quelle
1
Verbindungsserver sind für solche Dinge brutal. Ein möglicher Ansatz zur Lösung dieses Problems besteht darin, die Remotedaten über die Verbindung zum Verbindungsserver zu kopieren, indem INSERT INTO #t (a,b,c) SELECT a,b,c FROM LinkedServer.database.dbo.table WHERE x=ydie NOT EXISTS (...)Klausel mit dieser temporären Kopie der Datenbank ausgeführt wird.
Max Vernon
2
Ein wenig schüchtern im Moment, um eine Antwort von Max Vernon auf meinen Beitrag zu bekommen! Fanboy'ing beiseite. Es ist lustig, dass Sie das erwähnen, da ich diesen genauen Ansatz mehrmals verwendet habe, um das Beste aus diesen serverübergreifenden Situationen herauszuholen.
Robopim
1
Prost, @pimbrouwers - danke für deinen freundlichen Kommentar!
Max Vernon
5

Im Allgemeinen erstellt die Engine einen Ausführungsplan, der im Wesentlichen auf folgenden Elementen basiert:

  1. Die Anzahl der Zeilen in A und B
  2. Gibt es einen Index für A und / oder B.
  3. Die erwartete Anzahl von Ergebniszeilen (und Zwischenzeilen)
  4. Die Form der Eingabeabfrage (dh Ihre Frage)

Für 4):

Der Plan "nicht vorhanden" empfiehlt einen Plan auf der Grundlage von Suchvorgängen für Tabelle B. Dies ist eine gute Wahl, wenn Tabelle A klein und Tabelle B groß ist (und ein Index für B vorhanden ist).

Der "Antijoin" -Plan ist eine gute Wahl, wenn Tabelle A sehr groß oder Tabelle B sehr klein oder kein Index für B ist und eine große Ergebnismenge zurückgibt.

Es ist jedoch nur eine "Ermutigung", wie eine gewichtete Eingabe. Ein starker (1), (2), (3) trifft oft die Wahl für (4) Moot.

(Ignorieren Sie die Auswirkung Ihres Beispiels, das aufgrund des von @MaxVernon angesprochenen * verschiedene Spalten zurückgibt.)

crokusek
quelle