INNER JOIN ON vs WHERE clausola


941

Per semplicità, supponiamo che tutti i campi rilevanti lo siano NOT NULL.

Tu puoi fare:

SELECT
    table1.this, table2.that, table2.somethingelse
FROM
    table1, table2
WHERE
    table1.foreignkey = table2.primarykey
    AND (some other conditions)

O altro:

SELECT
    table1.this, table2.that, table2.somethingelse
FROM
    table1 INNER JOIN table2
    ON table1.foreignkey = table2.primarykey
WHERE
    (some other conditions)

Questi due funzionano allo stesso modo MySQL?


1
@Marco: eccolo qui
Alexander Malakhov il


18
Se ho capito correttamente, la prima variante è la sintassi implicita ANSI SQL-89 e la seconda variante è la sintassi di join esplicita ANSI SQL-92. Entrambi comporteranno lo stesso risultato in implementazioni SQL conformi ed entrambi comporteranno lo stesso piano di query in implementazioni SQL ben eseguite. Personalmente preferisco la sintassi SQL-89 ma molte persone preferiscono la sintassi SQL-92.
Mikko Rantalainen,

11
@Hogan stavo sottolineando i nomi ufficiali per diverse sintassi. Nessuna delle risposte ha esplicitamente indicato i nomi completi, quindi ho deciso di aggiungerli come commenti. Tuttavia, il mio commento non ha risposto alla domanda reale, quindi l'ho aggiunto come commento, non come risposta. (Le risposte più votate hanno affermazioni come "INNER JOIN is ANSI sintassi" e "implicita join ANSI sintassi è più vecchia" che non dice nulla perché entrambe le sintassi sono diverse sintassi ANSI.)
Mikko Rantalainen

Risposte:


710

INNER JOIN è la sintassi ANSI che dovresti usare.

È generalmente considerato più leggibile, soprattutto quando si uniscono molti tavoli.

Può anche essere facilmente sostituito con un OUTER JOINogni volta che si presenta una necessità.

La WHEREsintassi è più orientata al modello relazionale.

Un risultato di due tabelle JOINed è un prodotto cartesiano delle tabelle a cui viene applicato un filtro che seleziona solo quelle righe con colonne di unione corrispondenti.

È più facile vederlo con la WHEREsintassi.

Come nel tuo esempio, in MySQL (e in SQL in generale) queste due query sono sinonimi.

Inoltre, MySQL ha anche una STRAIGHT_JOINclausola.

Usando questa clausola, puoi controllare l' JOINordine: quale tabella viene scansionata nel loop esterno e quale si trova nel loop interno.

Non puoi controllarlo in MySQL usando la WHEREsintassi.


10
Grazie Quassnoi. Hai molti dettagli nel tuo ans; è giusto dire che "sì, quelle query sono equivalenti, ma dovresti usare il join interno perché è più leggibile e più facile da modificare"?
codice alleato

8
@allyourcode: per Oracle, SQL Server, MySQLe PostgreSQL- sì. Per altri sistemi, probabilmente, anche tu, ma è meglio controllare.
Quassnoi,

13
FWIW, l'utilizzo di virgole con condizioni di join nella WHEREclausola è anche nello standard ANSI.
Bill Karwin,

1
@Bill Karwin: la JOINparola chiave non faceva parte degli standard proprietari fino al passato più recente che potrebbe sembrare. Si è fatto strada Oraclesolo nella versione 9e PostgreSQLnella versione 7.2(entrambi rilasciati in 2001). L'aspetto di questa parola chiave faceva parte ANSIdell'adozione standard, ed è per questo che questa parola chiave è generalmente associata ANSI, nonostante il fatto che quest'ultima supporti anche la virgola come sinonimo CROSS JOIN.
Quassnoi,

9
Tuttavia, ANSI SQL-89 ha specificato join da eseguire con virgole e condizioni in una WHEREclausola (senza condizioni, un join equivale a un cross join, come hai detto). ANSI SQL-92 ha aggiunto la JOINparola chiave e la relativa sintassi, ma la sintassi in stile virgola è ancora supportata per la compatibilità con le versioni precedenti.
Bill Karwin,

182

Altri hanno sottolineato che INNER JOINaiuta la leggibilità umana, e questa è una priorità assoluta, sono d'accordo.
Vorrei provare a spiegare perché la sintassi del join è più leggibile.

Una SELECTquery di base è questa:

SELECT stuff
FROM tables
WHERE conditions

La SELECTclausola ci dice che cosa stiamo tornando; la FROMclausola ci dice da dove la stiamo ottenendo e la WHEREclausola ci dice da quali stiamo ricevendo.

JOIN è una dichiarazione sulle tabelle, su come sono legate insieme (concettualmente, in realtà, in una singola tabella).

Qualsiasi elemento di query che controlla le tabelle - da cui stiamo ottenendo elementi - appartiene semanticamente alla FROMclausola (e, naturalmente, è lì JOINche vanno gli elementi). Inserendo elementi di giunzione nella WHEREclausola si fondono il quale e il da dove , ecco perché JOINsi preferisce la sintassi.


7
Grazie per aver chiarito il motivo per cui il join interno è preferito Carl. Penso che il tuo ans sia implicito negli altri, ma esplicito di solito è meglio (sì, sono un fan di Python).
codice alleato

2
La semantica di ON e WHERE significano che per JOIN dopo l'ultimo OUTER JOIN non importa quale usi. Sebbene caratterizzi ON come parte di JOIN, è anche un filtro per un prodotto cartesiano. Sia ON che WHERE filtrano un prodotto cartesiano. Ma ON o una sottoselezione con WHERE devono essere utilizzati prima dell'ultimo OUTER JOIN. (I JOIN non sono coppie di colonne "on". Ogni due tabelle possono essere JOIN su QUALSIASI condizione. Questo è solo un modo per interpretare JOINs ON sulla parità delle colonne in particolare.)
philipxy,

Anche quando usi WHERE con lo stesso effetto di INNER JOIN, citerai le tue due tabelle nella parte FROM della query. Quindi, in sostanza, stai ancora insinuando dove stai ottenendo i tuoi dati nella clausola FROM, quindi immagino che non si possa dire che necessariamente "combina il quale e il da dove"
cybergeek654

@ArsenKhachaturyan Solo perché una parola chiave o un identificatore viene utilizzato nel testo non significa che sia un codice e necessita di un formato di codice. Questa è una scelta di formattazione che potrebbe andare in qualsiasi modo e se è ragionevole modificare qui, allora è giustificabile che ogni post sia costantemente modificato nell'altro formato - vale a dire, non è giustificabile. (Inoltre, il formato del codice in linea per parola può essere difficile da leggere.) Lo stesso vale per le interruzioni di paragrafo qui - non sono particolarmente chiare. Lo stesso con 'che' vs 'quello'. E i nomi dei linguaggi di programmazione non dovrebbero essere in formato codice. PS Hai aggiunto un'interruzione di riga per errore.
philipxy,

@philipxy come hai detto "non significa ...", ma ovviamente nessuno dei due significa che non può essere contrassegnato con la parola chiave code. Sì, è una scelta da fare, ma molti post vengono fatti senza sapere questo fatto. Quindi la mia decisione di apportare le modifiche non ha lo scopo di rompere nulla ma renderlo più leggibile. Se hai notato un'interruzione dopo aver formulato le modifiche, scusami e ovviamente puoi annullare tali modifiche.
Arsen Khachaturyan,

143

Applicazione di dichiarazioni condizionali in ON / DOVE

Qui ho spiegato le fasi di elaborazione della query logica.


Riferimento: Inside Microsoft® SQL Server ™ 2005 T-SQL Querying
Editore: Microsoft Press
Pub Data: 07 marzo 2006
Stampa ISBN-10: 0-7356-2313-9
Stampa ISBN-13: 978-0-7356-2313-2
Pagine: 640

All'interno di query T-SQL di Microsoft® SQL Server ™ 2005

(8)  SELECT (9) DISTINCT (11) TOP <top_specification> <select_list>
(1)  FROM <left_table>
(3)       <join_type> JOIN <right_table>
(2)       ON <join_condition>
(4)  WHERE <where_condition>
(5)  GROUP BY <group_by_list>
(6)  WITH {CUBE | ROLLUP}
(7)  HAVING <having_condition>
(10) ORDER BY <order_by_list>

Il primo aspetto evidente di SQL diverso dagli altri linguaggi di programmazione è l'ordine in cui il codice viene elaborato. Nella maggior parte dei linguaggi di programmazione, il codice viene elaborato nell'ordine in cui è scritto. In SQL, la prima clausola che viene elaborata è la clausola FROM, mentre la clausola SELECT, che appare per prima, viene elaborata quasi per ultima.

Ogni passaggio genera una tabella virtuale che viene utilizzata come input per il passaggio successivo. Queste tabelle virtuali non sono disponibili per il chiamante (applicazione client o query esterna). Solo la tabella generata dal passaggio finale viene restituita al chiamante. Se una determinata clausola non viene specificata in una query, il passaggio corrispondente viene semplicemente ignorato.

Breve descrizione delle fasi di elaborazione delle query logiche

Non preoccuparti troppo se la descrizione dei passaggi non sembra avere molto senso per ora. Questi sono forniti come riferimento. Le sezioni che seguono l'esempio dello scenario copriranno i passaggi in modo molto più dettagliato.

  1. FROM: un prodotto cartesiano (cross join) viene eseguito tra le prime due tabelle nella clausola FROM e, di conseguenza, viene generata la tabella virtuale VT1.

  2. ON: il filtro ON viene applicato a VT1. Solo le righe per le quali <join_condition>è TRUE vengono inserite in VT2.

  3. OUTER (join): se viene specificato un OUTER JOIN (al contrario di un CROSS JOIN o un INNER JOIN), le righe della tabella conservata o le tabelle per le quali non è stata trovata una corrispondenza vengono aggiunte alle righe da VT2 come righe esterne, generando VT3. Se vengono visualizzate più di due tabelle nella clausola FROM, i passaggi da 1 a 3 vengono applicati ripetutamente tra il risultato dell'ultimo join e la tabella successiva nella clausola FROM fino a quando non vengono elaborate tutte le tabelle.

  4. DOVE: il filtro DOVE viene applicato a VT3. Solo le righe per le quali <where_condition>è TRUE vengono inserite in VT4.

  5. GROUP BY: le righe da VT4 sono organizzate in gruppi in base all'elenco di colonne specificato nella clausola GROUP BY. Viene generato VT5.

  6. CUBE | ROLLUP: i supergruppi (gruppi di gruppi) vengono aggiunti alle righe da VT5, generando VT6.

  7. HAVING: il filtro HAVING viene applicato a VT6. Solo i gruppi per i quali <having_condition>è TRUE vengono inseriti in VT7.

  8. SELECT: l'elenco SELECT viene elaborato, generando VT8.

  9. DISTINCT: le righe duplicate vengono rimosse da VT8. Viene generato VT9.

  10. ORDER BY: le righe da VT9 sono ordinate in base all'elenco di colonne specificato nella clausola ORDER BY. Viene generato un cursore (VC10).

  11. INIZIO: il numero o la percentuale di righe specificati è selezionato dall'inizio di VC10. La tabella VT11 viene generata e restituita al chiamante.



Pertanto, (INNER JOIN) ON filtrerà i dati (il conteggio dei dati di VT verrà ridotto qui stesso) prima di applicare la clausola WHERE. Le condizioni di join successive verranno eseguite con dati filtrati che migliorano le prestazioni. Successivamente solo la condizione WHERE applicherà le condizioni del filtro.

(L'applicazione delle istruzioni condizionali in ON / DOVE non farà molta differenza in alcuni casi. Ciò dipende dal numero di tabelle che sono state unite e dal numero di righe disponibili in ciascuna tabella dei join)


10
"Pertanto, (INNER JOIN) ON filtrerà i dati (il conteggio dei dati di VT verrà ridotto qui stesso) prima di applicare la clausola WHERE." Non necessariamente. L'articolo riguarda l' ordine logico di elaborazione. Quando dici che una particolare implementazione farà una cosa prima di un'altra, stai parlando dell'ordine di elaborazione implementato . Le implementazioni possono effettuare qualsiasi ottimizzazione a loro piacimento, purché il risultato sia lo stesso se l'implementazione seguisse l'ordine logico. Joe Celko ha scritto molto su questo su Usenet.
Mike Sherrill 'Cat Recall',

@rafidheen "(INNER JOIN) ON filtrerà i dati ... prima di applicare la clausola WHERE ... che migliora le prestazioni." Buon punto. "Dopo di che solo la condizione WHERE applicherà le condizioni di filtro" E la clausola HAVING?
James

@James L'affermazione di rafidheen è sbagliata. Vedere "ottimizzazione del join" nel manuale. Anche i miei altri commenti su questa pagina. (E MikeSherrill'CatRecall''s). Tali descrizioni "logiche" descrivono il valore del risultato, non il modo in cui viene effettivamente calcolato. E tale comportamento di implementazione non è garantito per non cambiare.
philipxy,

67

La sintassi ANSI di join implicita è più vecchia, meno ovvia e sconsigliata.

Inoltre, l'algebra relazionale consente l'interscambiabilità dei predicati nella WHEREclausola e la INNER JOIN, quindi anche le INNER JOINquery conWHERE clausole possano riorganizzare i predicati dall'ottimizzatore.

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

A volte questo include rendere INNER JOINrelativamente "incompleto" e mettere alcuni dei criteri nelWHERE semplicemente 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.


16
Il tuo primo frammento mi fa decisamente più male al cervello. Qualcuno lo fa davvero? Se incontro qualcuno che lo fa, va bene per me batterlo sopra la testa?
codice alleato

3
Individuo i criteri in cui ha più senso. Se mi unisco a una tabella di ricerca snapshot temporalmente coerente (e non ho una vista o UDF che imponga la selezione di una data valida), includerò la data effettiva nel join e non in WHERE perché è inferiore probabilmente rimosso accidentalmente.
Cade Roux,

14
@allyourcode: sebbene sia raro vedere questo tipo di sintassi di join in INNER JOINs, è abbastanza comune per RIGHT JOINs e LEFT JOIN - la specifica di maggiori dettagli nel predicato join elimina la necessità di una sottoquery e impedisce che i join esterni vengano inavvertitamente trasformati in INNER JOINs. (Anche se concordo sul fatto che per i JOIN INNER quasi sempre inserisco c.State = 'NY' nella clausola WHERE)
Dave Markle,

1
@allyourcode Lo faccio sicuramente! E sono d'accordo con Cade .. Sono curioso di sapere se esiste una ragione decente per non farlo
Arth

31

I join impliciti (che è noto come la tua prima query) diventano molto più confusi, difficili da leggere e difficili da mantenere una volta che devi iniziare ad aggiungere più tabelle alla tua query. Immagina di fare la stessa query e lo stesso tipo di join su quattro o cinque diversi tavoli ... è un incubo.

L'uso di un join esplicito (il tuo secondo esempio) è molto più leggibile e facile da mantenere.


48
Non potrei essere più in disaccordo. La sintassi JOIN è estremamente prolissa e difficile da organizzare. Ho un sacco di domande che uniscono 5, 10, anche 15 tabelle usando i join della clausola WHERE e sono perfettamente leggibili. Riscrivere una query del genere utilizzando una sintassi JOIN si traduce in un pasticcio confuso. Il che dimostra che non esiste una risposta giusta a questa domanda e che dipende più da cosa ti senti a tuo agio.
Noah Yetter,

33
Noah, penso che potresti essere in minoranza qui.
opaco b

2
Ottengo +1 per Matt e Noah. Mi piace la diversità :). Vedo da dove viene Noè; inner join non aggiunge nulla di nuovo alla lingua ed è decisamente più dettagliato. D'altra parte, può rendere la tua condizione 'where' molto più breve, il che significa che è più facile da leggere.
codice alleato

5
Suppongo che qualsiasi DBMS sano traduca le due query nello stesso piano di esecuzione; tuttavia in realtà ogni DBMS è diverso e l'unico modo per saperlo con certezza è effettivamente esaminare il piano di esecuzione (cioè, dovrete testarlo da soli).
matt b

È vero come ha suggerito @rafidheen in un'altra risposta (quella con la sequenza dettagliata dell'esecuzione di SQL) che i JOIN vengono filtrati uno alla volta, riducendo la dimensione delle operazioni di join rispetto a un join cartesiano completo di 3 o più tabelle, con il filtro DOVE viene applicato retroattivamente? In tal caso, suggerirebbe che JOIN offre miglioramenti delle prestazioni (oltre a vantaggi nei join sinistra / destra, come indicato anche in un'altra risposta).
James

26

Sottolineerò inoltre che l'utilizzo della sintassi precedente è più soggetto a errori. Se si utilizzano join interni senza una clausola ON, verrà visualizzato un errore di sintassi. Se usi la sintassi precedente e dimentichi una delle condizioni di join nella clausola where, otterrai un cross join. Gli sviluppatori spesso risolvono questo problema aggiungendo la parola chiave distinta (anziché correggere il join perché non si rendono ancora conto che il join stesso è interrotto) che potrebbe sembrare risolvere il problema, ma rallenterà notevolmente la query.

Inoltre, per la manutenzione se si dispone di un cross join nella vecchia sintassi, come farà il manutentore a sapere se si intende averne uno (ci sono situazioni in cui sono necessari cross join) o se si è trattato di un incidente che deve essere risolto?

Permettetemi di indicarvi questa domanda per capire perché la sintassi implicita è errata se usate i join di sinistra. Sybase * = secondo lo standard Ansi con 2 diversi tavoli esterni per lo stesso tavolo interno

Inoltre (personale rant qui), lo standard che utilizza i join espliciti ha più di 20 anni, il che significa che la sintassi dei join impliciti è stata superata per questi 20 anni. Scriveresti il ​​codice dell'applicazione usando una sintassi obsoleta da 20 anni? Perché vuoi scrivere il codice del database che è?


3
@HLGEM: Mentre sono completamente d'accordo sul fatto che i JOIN espliciti siano migliori, ci sono casi in cui devi solo usare la vecchia sintassi. Un esempio reale: ANSI JOIN è entrato in Oracle solo nella versione 9i che è stata rilasciata nel 2001 e fino a solo un anno fa (16 anni dal momento in cui è stato pubblicato lo standard) ho dovuto supportare un gruppo di installazioni 8i per le quali avevamo per rilasciare aggiornamenti critici. Non volevo mantenere due serie di aggiornamenti, quindi abbiamo sviluppato e testato gli aggiornamenti su tutti i database, incluso 8i, il che significa che non siamo stati in grado di utilizzare ANSI JOINs.
Quassnoi,

+1 punto interessante quando si sottolinea che la sintassi senza INNER JOIN è più soggetta a errori. Sono confuso riguardo alla tua ultima frase quando dici "... lo standard che utilizza i join espliciti ha 17 anni". quindi stai suggerendo di usare la parola chiave INNER JOIN o no?
Marco Demaio,

1
@Marco Demaio, sì, usa sempre INNER JOIN o JOIN (questi due sono uguali) o LEFT JOIN o RIGHT JOIN o CROSS JOIN e non utilizzare mai i join virgola impliciti.
HLGEM,

2
"Perché vuoi scrivere il codice del database che ha [20 anni]?" - Ho notato che scrivi SQL usando HAVINGquale è stato "obsoleto" da quando SQL ha iniziato a supportare le tabelle derivate. Ho anche notato che non usi NATURAL JOINanche se direi che è diventato INNER JOIN"obsoleto". Sì, hai le tue ragioni (non c'è bisogno di dichiararle di nuovo qui!): Il mio punto è che anche a chi piace usare la sintassi più vecchia hanno le loro ragioni e l'età relativa della sintassi è di scarsa importanza.
giorno

1
DOVE è ancora nello standard (mostrami dove non lo è). Quindi, apparentemente nulla di obsoleto. Inoltre, "piuttosto che correggere il join" mi mostra uno sviluppatore che dovrebbe essere tenuto lontano dai DBMS in generale, molto lontano.
Jürgen A. Erhard,

12

Hanno un diverso significato leggibile dall'uomo.

Tuttavia, a seconda di Query Optimizer, potrebbero avere lo stesso significato per la macchina.

Dovresti sempre programmare per essere leggibile.

Vale a dire, se si tratta di una relazione incorporata, utilizzare il join esplicito. se si esegue la corrispondenza su dati debolmente correlati, utilizzare la clausola where.


11

Lo standard SQL: 2003 ha modificato alcune regole di precedenza in modo che un'istruzione JOIN abbia la precedenza su un join "virgola". Ciò può effettivamente modificare i risultati della query in base alla configurazione. Ciò causa alcuni problemi ad alcune persone quando MySQL 5.0.12 è passato all'adesione allo standard.

Quindi, nel tuo esempio, le tue domande funzionerebbero allo stesso modo. Ma se hai aggiunto una terza tabella: SELEZIONA ... DA table1, table2 UNISCITI table3 SU ... DOVE ...

Prima di MySQL 5.0.12, table1 e table2 venivano prima uniti, quindi table3. Ora (5.0.12 e successivi), table2 e table3 vengono unite prima, quindi table1. Non cambia sempre i risultati, ma può e potresti anche non rendertene conto.

Non uso più la sintassi "virgola", optando per il tuo secondo esempio. È comunque molto più leggibile, le condizioni JOIN sono con i JOIN, non separate in una sezione di query separata.


SQL standard non è cambiato. MySQL aveva sbagliato e ora ha ragione. Vedi il manuale di MySQL.
Philipxy,

4

So che stai parlando di MySQL, ma comunque: in Oracle 9 i join espliciti e i join impliciti genererebbero piani di esecuzione diversi. AFAIK che è stato risolto in Oracle 10+: non c'è più tale differenza.


1

La sintassi del join ANSI è sicuramente più portatile.

Sto attraversando un aggiornamento di Microsoft SQL Server e vorrei anche menzionare che la sintassi = * e * = per i join esterni in SQL Server non è supportata (senza modalità di compatibilità) per il server sql 2005 e versioni successive.


2
Anche in SQL Server 2000, = e = potrebbero dare risultati errati e non dovrebbero mai essere usati.
HLGEM,

2
*=e =*non erano mai ANSI e non erano mai una buona notazione. Ecco perché era necessario ON - per OUTER JOINs in assenza di subselect (che sono stati aggiunti contemporaneamente, quindi non sono effettivamente necessari in CROSS & INNER JOINs)
philipxy,

1

Se stai spesso programmando stored procedure dinamiche, ti innamorerai del tuo secondo esempio (usando where). Se hai vari parametri di input e un sacco di confusione, questo è l'unico modo. In caso contrario, entrambi eseguiranno lo stesso piano di query, quindi non vi è alcuna differenza evidente nelle query classiche.

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.