2012-04-24 24 views
39

Supponendo che ho il seguente codice T-SQL:clausola WHERE vs ON quando si utilizza JOIN

SELECT * FROM Foo f 
INNER JOIN Bar b ON b.BarId = f.BarId; 
WHERE b.IsApproved = 1; 

Il seguente si ritorna anche lo stesso insieme di righe:

SELECT * FROM Foo f 
INNER JOIN Bar b ON (b.IsApproved = 1) AND (b.BarId = f.BarId); 

questo potrebbe non essere il miglior esempio di caso qui, ma c'è qualche differenza di prestazioni tra questi due?

+2

Ecco una domanda simile: http://stackoverflow.com/questions/2509987/which-sql-query-is-faster-filter-on-join-criteria-or-where-clause –

+11

La macchina lo capirà e ottimizzarlo correttamente. Tuttavia, per gli umani che dovranno eseguire il debug \ modify \ supportano il tuo codice da anni, mantieni le condizioni di filtraggio in 'WHERE' e unisciti alle condizioni in' ON'. –

+0

@ KM. Non so sempre come distinguere tra una condizione di join e un filtro. Ad esempio [in questa risposta] (http://stackoverflow.com/a/9303069/119477) Penso che sia meglio nel join, quindi una "condizione di Join", quindi? [Ecco un altro esempio] (http://stackoverflow.com/a/6473403/119477) che non so nemmeno come riscrivere l'equivalente clausola where. –

risposta

28

No, il Query Optimizer è abbastanza intelligente da scegliere lo stesso piano di esecuzione per entrambi gli esempi.

È possibile utilizzare SHOWPLAN per verificare il piano di esecuzione.


Tuttavia, si dovrebbe mettere tutti si uniscono collegamento sulla clausola ON e tutte le restrizioni alla clausola di WHERE.

+2

Mi picchia. Anche se preferisco, andrei con il JOIN in quanto è più descrittivo. – Ste

+1

Grazie! Immagina una situazione con 7 o 8 INNER JOINS. La tua risposta è applicabile anche a tali situazioni? – tugberk

+13

@Ste IMO, in realtà è più complicato mettere tutto in "JOIN". Usa 'JOIN' per relazionarsi alle tabelle in una query. Usa 'WHERE' per filtrare i risultati. È quando mescoli i due e usi ** solo ** l'uno o l'altro che le query diventano difficili da leggere. – Yuck

5
SELECT * FROM Foo f 
INNER JOIN Bar b ON b.BarId = f.BarId 
WHERE b.IsApproved = 1; 

Questa è la soluzione migliore. È facile da leggere e facile da modificare. Nel mondo degli affari questo è ciò che vorresti fare. Per quanto riguarda le prestazioni sono uguali però.

+0

Nella mia situazione attuale, preferisco la clausola WHERE ma non posso evitare di chiedermi se esiste un diff diff. Grazie! – tugberk

42

Basta fare attenzione alla differenza con i giunti esterni. Una query in cui viene aggiunto un filtro di b.IsApproved (sul tavolo a destra, Bar) alla ON condizione del JOIN:

SELECT * 
FROM Foo f 
LEFT OUTER JOIN Bar b ON (b.IsApproved = 1) AND (b.BarId = f.BarId); 

è NON lo stesso come mettere il filtro nella clausola WHERE:

SELECT * 
FROM Foo f 
LEFT OUTER JOIN Bar b ON (b.BarId = f.BarId) 
WHERE (b.IsApproved = 1); 

Dal momento che per 'fallito' outer join a Bar (cioè dove non c'è b.BarId per un f.BarId), questo lascerà b.IsApproved come NULL per tutti come fa Iled unisce le righe e queste verranno quindi filtrate.

Un altro modo di vedere questo è che per la prima interrogazione, LEFT OUTER JOIN Bar b ON (b.IsApproved = 1) AND (b.BarId = f.BarId) sarà sempre tornare le righe della tabella SINISTRA, dal momento che garantisce LEFT OUTER JOIN le righe della tabella SINISTRA saranno restituiti anche se il join non riesce. Tuttavia, l'effetto dell'aggiunta diallo stato LEFT OUTER JOIN a condizione è di escludere qualsiasi colonna della tabella destra quando (b.IsApproved = 1) è falso, vale a dire secondo le stesse regole normalmente applicate a una condizione LEFT JOIN su (b.BarId = f.BarId).

Aggiornamento: Per completare la domanda posta da Conrad, il LOJ equivalente per un filtro opzionale sarebbe:

SELECT * 
FROM Foo f 
LEFT OUTER JOIN Bar b ON (b.BarId = f.BarId) 
WHERE (b.IsApproved IS NULL OR b.IsApproved = 1); 

cioè la WHERE clausola deve considerare sia la condizione se il join non riesce (NULL) e il filtro deve essere ignorato e dove l'unione riesce e il filtro deve essere applicato.(b.IsApproved o b.BarId potrebbe essere testato per NULL)

ho messo un SqlFiddle together here che dimostra le differenze tra i vari piazzamenti del b.IsApproved del filtro rispetto al JOIN.

+0

+1, buon punto! –

+0

Ottimo punto. Se si inseriscono i criteri di verifica dei dati di test da un join esterno nel join esterno stesso, si otterranno più file di quanto previsto poiché verranno restituiti tutti gli Foo indipendentemente dallo stato o dall'esistenza di Bar. Quando il filtro viene specificato separatamente dall'unione, le righe delle due tabelle vengono prima unite e quindi il filtro rimuove l'intera riga dalla tabella in cui i criteri non vengono soddisfatti. – KeithS

+1

@nonnb Ok ma se hai corretto la clausola WHERE sulla seconda query su 'WHERE b.IsApproved = 1 o b.BarId is Null' è lo stesso. Ora quale fai? –

0

Mi sembra che alcuni casi in cui l'ottimizzatore non fosse abbastanza intelligente anche nelle versioni recenti di MSSQL - e la differenza di prestazioni era mostruosa.

Ma questa è un'eccezione, la maggior parte del tempo con SQL Server Optimizer risolverà il problema e otterrà il piano giusto.

Quindi mantenere la politica di utilizzo dei filtri nella clausola WHERE e ottimizzare quando necessario.

0

Ho appena eseguito un test di una query su quattro tabelle: una tabella primaria con tre JOIN INNER e un totale di quattro parametri e ha confrontato i piani di esecuzione di entrambi gli approcci (utilizzando i criteri di filtro nell'On della JOIN, e poi anche nella clausola WHERE).

I piani di esecuzione sono esattamente gli stessi. Ho eseguito questo su SQL Server 2008 R2.

Problemi correlati