Condizioni all'interno di JOIN o WHERE


194

C'è qualche differenza (performance, best practice, ecc ...) tra l'inserimento di una condizione nella clausola JOIN e la clausola WHERE?

Per esempio...

-- Condition in JOIN
SELECT *
FROM dbo.Customers AS CUS
INNER JOIN dbo.Orders AS ORD 
ON CUS.CustomerID = ORD.CustomerID
AND CUS.FirstName = 'John'

-- Condition in WHERE
SELECT *
FROM dbo.Customers AS CUS
INNER JOIN dbo.Orders AS ORD 
ON CUS.CustomerID = ORD.CustomerID
WHERE CUS.FirstName = 'John'

Quale preferisci (e forse perché)?


4
Hai eseguito le due query? Hai controllato i piani di esecuzione generati dalle due query? Che cosa hai osservato?
S.Lott

22
@ S.Lott, questa query è solo a scopo di esempio. Mi chiedo solo "in generale" quale sia il metodo preferito, se presente.
Steve Dignan,

1
@Steve Dignan: dovresti confrontarlo con dati di esempio e guardare i piani di query. La risposta sarà molto, molto chiara. E - bonus - avrai un pezzo di codice che puoi riutilizzare quando sorgono situazioni più complesse.
S.Lott

1
Personalmente metterei la condizione nella clausola JOIN se la condizione descrive la relazione. Le condizioni generiche che filtrano solo il set di risultati andrebbero quindi alla parte WHERE. Ad esempioFROM Orders JOIN OrderParties ON Orders.Id = OrderParties.Order AND OrderParties.Type = 'Recipient' WHERE Orders.Status = 'Canceled'
Glutexo,

Risposte:


154

L'algebra relazionale consente l'interscambiabilità dei predicati nella WHEREclausola e la INNER JOIN, quindi anche le INNER JOINquery con WHEREclausole possono far risistemare i predicati dall'ottimizzatore in modo che possano essere già esclusi durante il JOINprocesso.

Ti consiglio di scrivere le domande nel modo più leggibile possibile.

A volte ciò include rendere INNER JOINrelativamente "incompleto" e inserire alcuni dei criteri WHEREsemplicemente per rendere più facilmente gestibili gli elenchi dei criteri di filtro.

Ad esempio, anziché:

SELECT *
FROM Customers c
INNER JOIN CustomerAccounts ca
    ON ca.CustomerID = c.CustomerID
    AND c.State = 'NY'
INNER JOIN Accounts a
    ON ca.AccountID = a.AccountID
    AND a.Status = 1

Scrivi:

SELECT *
FROM Customers c
INNER JOIN CustomerAccounts ca
    ON ca.CustomerID = c.CustomerID
INNER JOIN Accounts a
    ON ca.AccountID = a.AccountID
WHERE c.State = 'NY'
    AND a.Status = 1

Ma dipende, ovviamente.


7
Non si tratta solo di query pulite o leggibilità, ma di prestazioni. mettere le condizioni in join migliora le prestazioni per grandi quantità di dati con tabelle adeguatamente indicizzate.
Shahdat,

1
Ho appena eseguito rapporti mensili sulle vendite unendo 5-6 tabelle su pochi milioni di record. Perf migliora del 30% - sql server 2012
Shahdat

2
@Shahdat se stai ottenendo una differenza di prestazioni così significativa spostando le condizioni del filtro dalla clausola where al join interno, devi pubblicare i piani di esecuzione.
Cade Roux,

4
@Cade Ho studiato i piani di esecuzione - entrambi gli scenari mostrano lo stesso costo. Eseguo le query più volte sembra che impieghino lo stesso tempo. In precedenza, stavo eseguendo le query sulla produzione e ottenevo differenze significative nelle prestazioni perché il database veniva utilizzato dagli utenti live. Scusa per quella confusione.
Shahdat,

4
Questa risposta è giusta per i JOIN INNER ma non per i join left / right.
Dal

123

Per i join interni non ho notato alcuna differenza (ma come per tutte le ottimizzazioni delle prestazioni, è necessario controllare il database in base alle proprie condizioni).

Tuttavia, se si inserisce la condizione, la differenza è enorme se si utilizzano i join destro o sinistro. Ad esempio, considera queste due query:

SELECT *
FROM dbo.Customers AS CUS 
LEFT JOIN dbo.Orders AS ORD 
ON CUS.CustomerID = ORD.CustomerID
WHERE ORD.OrderDate >'20090515'

SELECT *
FROM dbo.Customers AS CUS 
LEFT JOIN dbo.Orders AS ORD 
ON CUS.CustomerID = ORD.CustomerID
AND ORD.OrderDate >'20090515'

Il primo ti darà solo quei record che hanno un ordine datato dopo il 15 maggio 2009, convertendo così il join sinistro in un join interno.

Il secondo fornirà quei record più eventuali clienti senza ordini. Il set di risultati è molto diverso a seconda di dove si inserisce la condizione. (Seleziona * è solo a scopo di esempio, ovviamente non dovresti usarlo nel codice di produzione.)

L'eccezione a ciò è quando si desidera vedere solo i record in una tabella ma non nell'altra. Quindi si utilizza la clausola where per la condizione, non il join.

SELECT *
FROM dbo.Customers AS CUS 
LEFT JOIN dbo.Orders AS ORD 
ON CUS.CustomerID = ORD.CustomerID
WHERE ORD.OrderID is null

Grazie per aver spiegato con esempi
Rennish Joseph,

1
"convertendo così il join sinistro in un join interno". Come? Puoi elaborare un po '?
user1451111

@ user1451111 Scopri cosa restituisce JOIN SINISTRA / DESTRA: righe INNER JOIN più righe di tabella sinistra / destra senza pari estese da NULL. FULL JOIN restituisce le righe INNER JOIN UNION TUTTE le righe della tabella sinistra e destra senza pari estese da NULL. Sapere sempre quale INNER JOIN si desidera come parte di un OUTER JOIN. Un WHERE o ON che richiede che una colonna eventualmente NULL estesa non sia NULL dopo un OUTER JOIN ON rimuove qualsiasi riga estesa da NULL, ovvero lascia solo le righe INNER JOIN, ovvero "trasforma un OUTER JOIN in un INNER JOIN".
philipxy,

1
@ user1451111 o, in termini più semplici: A left join Bogni riga da A è unita a ogni riga corrispondente da B. Se B non ha una riga corrispondente, allora le colonne A hanno un valore ma ogni colonna da B su quella riga mostra come valori NULL. Se hai scritto, where B.somecolumn = ‘somevalue’hai un NULL (B.somecolumn) confrontato con 'somevalue'. Qualsiasi cosa confrontata con NULL è falsa, quindi tutte le righe in cui non vi è alcuna riga B corrispondente per la riga A, vengono eliminate e i risultati ottenuti sono gli stessi che darebbe un JOIN INNER, quindi il join esterno è diventato uno interno
Caius Jard,

sì, ho verificato che i risultati siano gli stessi per: SELECT funds.id, prospects.id FROM prospettive fundsinterne interne su (prospects.id = funds.lead_id e prospects.is_manual = 'no') e SELECT funds.id, prospects.id DA fundssinistra unisciti a prospects su (prospects.id = funds.lead_id) dove prospects.is_manual = 'no'
Rohit Dhiman,

25

La maggior parte dei prodotti RDBMS ottimizzerà entrambe le query in modo identico. In "SQL Performance Tuning" di Peter Gulutzan e Trudy Pelzer, hanno testato più marchi di RDBMS e non hanno riscontrato differenze di prestazioni.

Preferisco mantenere le condizioni di join separate dalle condizioni di restrizione della query.

Se si utilizza a OUTER JOINvolte è necessario inserire le condizioni nella clausola di join.


1
Sono d'accordo con te sul fatto che sintatticamente sia più pulito e devo rimandare alla tua conoscenza di quel libro e alla tua altissima reputazione, ma riesco a pensare a 4 query nell'ultima settimana con piani di esecuzione, tempi CPU e letture logiche molto diversi quando Mi sono trasferito dove predicati al join.
marr75,

2
Mi stavi chiedendo delle migliori pratiche. Non appena si inizia a testare come funziona un'implementazione RDBMS specifica, altre persone hanno dato il consiglio corretto: benchmark.
Bill Karwin,

12

DOVE filtra dopo che si è verificato il JOIN.

Filtro su JOIN per impedire l'aggiunta di righe durante il processo JOIN.


10
Semanticamente, vengono impediti durante il processo INNER JOIN, ma l'ottimizzatore può riorganizzare INNER JOIN e DOVE predice a piacimento, quindi l'ottimizzatore è libero di escluderli in seguito, se lo desidera.
Cade Roux,

1
Cade Roux: giusto. Spesso ciò che scrivi in ​​SQL non è quello che ti darà l'ottimizzatore quando tutto sarà detto e fatto. Suppongo quindi che questo sarebbe giusto in un mondo di tutta la teoria, mentre la tua risposta è ovviamente più corretta nel mondo degli ottimizzatori di query automatici :)
TheTXI

Mi piace questa spiegazione della condizione nelON
Robert Rocha del

3

Preferisco JOIN per unire tabelle / viste complete e quindi utilizzare WHERE per introdurre il predicato dell'insieme risultante.

Sembra sintatticamente più pulito.


2

In genere vedo un aumento delle prestazioni durante il filtraggio sul join. Soprattutto se puoi unirti su colonne indicizzate per entrambe le tabelle. Dovresti essere in grado di ridurre le letture logiche eseguendo anche la maggior parte delle query, che è, in un ambiente ad alto volume, un indicatore di prestazioni molto migliore rispetto ai tempi di esecuzione.

Sono sempre leggermente divertito quando qualcuno mostra il suo benchmarking SQL e ha eseguito entrambe le versioni di uno sproc 50.000 volte a mezzanotte sul server di sviluppo e confrontando i tempi medi.


0

Mettere la condizione nel join mi sembra "semanticamente sbagliato", dato che non è quello per cui i JOIN sono "per". Ma è molto qualitativo.

Ulteriore problema: se si decide di passare da un join interno a, diciamo, a un join destro, la condizione all'interno del JOIN potrebbe portare a risultati imprevisti.


3
A volte questi risultati sono in qualche modo "attesi" e talvolta anche "intenzionali" (ad esempio con join esterni, in cui la condizione WHERE ha una semantica diversa rispetto alla condizione JOIN).
Marcel Toth,

0

Le iscrizioni sono più veloci secondo me quando hai un tavolo più grande. In realtà non fa molta differenza, soprattutto se si ha a che fare con una tabella piuttosto piccola. Quando ho appreso per la prima volta dei join, mi è stato detto che le condizioni nei join sono proprio come le condizioni della clausola where e che potrei usarle in modo intercambiabile se la clausola where fosse specifica su quale tabella applicare la condizione.


-4

È meglio aggiungere la condizione nel Join. Le prestazioni sono più importanti della leggibilità. Per set di dati di grandi dimensioni, è importante.


1
Hai qualche tipo di prova, ricerca in che modo il posizionamento dei predicati menzionati influisce sulle prestazioni?
Zso,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.