it-swarm.it

Best practice tra l'utilizzo di LEFT JOIN o NOT EXISTS

Esiste una procedura ottimale tra l'utilizzo di un formato LEFT JOIN o NOT EXISTS?

Quali sono i vantaggi dell'utilizzo l'uno rispetto all'altro?

Se nessuno, quale dovrebbe essere preferito?

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)

Sto usando query in Access contro un database di SQL Server.

72

La differenza più grande non è nel join vs non esiste, è (come scritto), il SELECT *.

Nel primo esempio, ottieni tutte le colonne da entrambi A e B, mentre nel secondo esempio, tu ottieni solo colonne da A.

In SQL Server, la seconda variante è leggermente più veloce in un semplice esempio inventato:

Creare due tabelle di esempio:

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

Inserisci 10.000 righe in ogni tabella:

INSERT INTO dbo.A DEFAULT VALUES;
GO 10000

INSERT INTO dbo.B DEFAULT VALUES;
GO 10000

Rimuovi ogni 5a fila dalla seconda tabella:

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;

Esegui le due varianti dell'istruzione SELECT di prova:

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);

Piani di esecuzione:

enter image description here

La seconda variante non deve eseguire l'operazione di filtro poiché può utilizzare l'operatore anti-semi join sinistro.

60
Max Vernon

Logicamente sono identici, ma NOT EXISTS è più vicino all'AntiSemiJoin che stai chiedendo ed è generalmente preferito. Inoltre, evidenzia meglio che non è possibile accedere alle colonne in B, poiché viene utilizzato solo come filtro (invece di averle disponibili con valori NULL).

Molti anni fa (SQL Server 6.0 ish), LEFT JOIN è stato più veloce, ma non è stato così per molto tempo. Questi giorni, NOT EXISTS è leggermente più veloce.


L'impatto maggiore in Access è che il metodo JOIN deve completare il join prima di filtrarlo, costruendo il set unito in memoria. Utilizzando NOT EXISTS controlla la riga ma non alloca spazio per le colonne. Inoltre, smette di cercare una volta trovata una riga. Le prestazioni variano un po 'di più in Access, ma una regola generale è che NOT EXISTS tende ad essere un po 'più veloce. Sarei meno propenso a dire che è "best practice", poiché ci sono più fattori coinvolti.

25
Rob Farley

Un'eccezione che ho notato al NOT EXISTS essendo superiore (comunque marginalmente) a LEFT JOIN ... WHERE IS NULL è quando si utilizzano server collegati .

Dall'esame dei piani di esecuzione, sembra che NOT EXISTS L'operatore viene eseguito in modo nidificato. Per cui viene eseguito su una riga per riga (che suppongo abbia senso).

Esempio di piano di esecuzione che dimostra questo comportamento: enter image description here

7
pimbrouwers

In generale, il motore creerà un piano di esecuzione basato essenzialmente su:

  1. Il numero di righe in A e B
  2. Se esiste un indice su A e/o B.
  3. Il numero previsto di righe dei risultati (e righe intermedie)
  4. Il modulo della query di input (ovvero la tua domanda)

Per (4):

Il piano "non esiste" incoraggia un piano basato sulla ricerca nella tabella B. Questa è una buona scelta quando la tabella A è piccola e la tabella B è grande (e un indice esiste su B).

Il piano "antijoin" è una buona scelta quando la tabella A è molto grande o la tabella B è molto piccola o nessun indice su B e restituisce un set di risultati di grandi dimensioni.

Tuttavia è solo un "incoraggiamento", come un input ponderato. Un forte (1), (2), (3) spesso fa la scelta per (4) moot.

(Ignorando l'effetto del tuo esempio restituendo colonne diverse a causa del *, indirizzato dalla risposta @MaxVernon.).

5
crokusek