SQL lasciato join vs più tabelle sulla riga FROM?


256

La maggior parte dei dialetti SQL accetta entrambe le seguenti query:

SELECT a.foo, b.foo
FROM a, b
WHERE a.x = b.x

SELECT a.foo, b.foo
FROM a
LEFT JOIN b ON a.x = b.x

Ora, ovviamente, quando è necessario un join esterno, è richiesta la seconda sintassi. Ma quando faccio un join interno, perché dovrei preferire la seconda sintassi alla prima (o viceversa)?


1
Guffa: Come l'hai trovato? Sebbene la mia domanda sia più una buona pratica che "come faccio"
jmucchiello,

Dal momento che è la migliore pratica, si prega di rendere questo un Wiki.
Binoj Antony,

1
Non credo che nessuno abbia commentato le prestazioni di questi due. Qualcuno può confermare o citare qualcosa di ragionevole riguardo a differenze significative?
ahnbizcad,

@ahnbizcad Le due query fornite non fanno la stessa cosa. Il primo restituisce lo stesso di un INNER JOIN ON. L'implementazione è specifica della versione di DBMS e anche in questo caso ha poche garanzie. Ma le trasformazioni DBMS che equivalgono ai casi di virgola vs INNER JOIN ON / WHERE vs CROSS JOIN WHERE sono banali. Informazioni sull'ottimizzazione / implementazione delle query del database relazionale
philipxy,

hai ricevuto una raccomandazione sulle risorse? manuali giganteschi e densi sono il motivo per cui provo ad imparare da qui.
ahnbizcad,

Risposte:


319

La vecchia sintassi, con un semplice elenco delle tabelle e l'utilizzo della WHEREclausola per specificare i criteri di join, è stata deprecata nella maggior parte dei database moderni.

Non è solo per lo spettacolo, la vecchia sintassi ha la possibilità di essere ambigua quando si utilizzano i join INNER e OUTER nella stessa query.

Lasciate che vi faccia un esempio.

Supponiamo che tu abbia 3 tabelle nel tuo sistema:

Company
Department
Employee

Ogni tabella contiene numerose righe, collegate tra loro. Hai più società e ogni azienda può avere più dipartimenti e ogni reparto può avere più dipendenti.

Ok, quindi ora vuoi fare quanto segue:

Elencare tutte le società e includere tutti i loro dipartimenti e tutti i loro dipendenti. Nota che alcune aziende non hanno ancora dipartimenti, ma assicurati di includerli anche tu. Assicurati di recuperare solo i reparti con dipendenti, ma elenca sempre tutte le aziende.

Quindi fai questo:

SELECT * -- for simplicity
FROM Company, Department, Employee
WHERE Company.ID *= Department.CompanyID
  AND Department.ID = Employee.DepartmentID

Si noti che l'ultimo è un join interno, al fine di soddisfare i criteri che si desidera solo dipartimenti con le persone.

Ok, quindi cosa succede adesso. Bene, il problema è che dipende dal motore del database, da Query Optimizer, dagli indici e dalle statistiche delle tabelle. Lasciatemi spiegare.

Se Query Optimizer determina che il modo per farlo è innanzitutto prendere un'azienda, quindi trovare i reparti e quindi unire i dipendenti con i dipendenti interni, non si otterranno aziende che non dispongono di dipartimenti.

La ragione di ciò è che la WHEREclausola determina quali righe finiscono nel risultato finale, non singole parti delle righe.

E in questo caso, a causa del join sinistro, la colonna Department.ID sarà NULL, quindi quando si tratta di INNER JOIN to Employee, non c'è modo di soddisfare quel vincolo per la riga Employee, e quindi non lo farà apparire.

D'altra parte, se l'ottimizzatore delle query decide di affrontare prima l'adesione al reparto dipendente e quindi eseguire un'unione a sinistra con le aziende, le vedrai.

Quindi la vecchia sintassi è ambigua. Non c'è modo di specificare ciò che si desidera, senza occuparsi di suggerimenti per le query, e alcuni database non hanno alcun modo.

Inserisci la nuova sintassi, con questa puoi scegliere.

Ad esempio, se vuoi tutte le aziende, come indicato nella descrizione del problema, questo è ciò che scriveresti:

SELECT *
FROM Company
     LEFT JOIN (
         Department INNER JOIN Employee ON Department.ID = Employee.DepartmentID
     ) ON Company.ID = Department.CompanyID

Qui si specifica che si desidera che l'unione reparto-dipendente venga eseguita come un'unica unione, quindi si lasciano unire i risultati con le società.

Supponiamo inoltre che desideri solo reparti che contengano la lettera X nel loro nome. Ancora una volta, con i join di vecchio stile, rischi di perdere anche l'azienda, se non ha dipartimenti con una X nel suo nome, ma con la nuova sintassi, puoi farlo:

SELECT *
FROM Company
     LEFT JOIN (
         Department INNER JOIN Employee ON Department.ID = Employee.DepartmentID
     ) ON Company.ID = Department.CompanyID AND Department.Name LIKE '%X%'

Questa clausola aggiuntiva viene utilizzata per l'unione, ma non è un filtro per l'intera riga. Pertanto, la riga potrebbe essere visualizzata con le informazioni sull'azienda, ma potrebbe contenere NULL in tutte le colonne del reparto e dei dipendenti per quella riga, perché non esiste un reparto con una X nel suo nome per quella società. Questo è difficile con la vecchia sintassi.

Questo è il motivo per cui, tra gli altri fornitori, Microsoft ha deprecato la vecchia sintassi del join esterno, ma non la vecchia sintassi del join interno, a partire da SQL Server 2005 e versioni successive. L'unico modo per comunicare con un database in esecuzione su Microsoft SQL Server 2005 o 2008, utilizzando la sintassi del join esterno di vecchio stile, è impostare quel database in modalità di compatibilità 8.0 (ovvero SQL Server 2000).

Inoltre, alla vecchia maniera, lanciando un sacco di tabelle in Query Optimizer, con un mucchio di clausole WHERE, era simile a dire "eccoti, fai il meglio che puoi". Con la nuova sintassi, Query Optimizer ha meno lavoro da fare per capire quali parti vanno insieme.

Così il gioco è fatto.

LEFT and INNER JOIN è l'onda del futuro.


28
"è deprecato nella maggior parte dei database moderni." --- solo curioso, quali?
zerkms,

10
perdonami, non ho familiarità con l'operatore * =, cosa fa? Grazie!
ultrajohn,

9
Stella = e = Stella sono (bene erano) giunti esterni destro e sinistro, oppure è sinistra e destra? Sono stato deprecato per anni, non li uso da SQL Server 6.
Tony Hopkinson,

3
La virgola non è deprecata. La OUTER JOINsintassi mai standard *=/ =*/ *=*è obsoleta.
philipxy,

1
Questa risposta non risponde nemmeno alla domanda, che non riguarda i join esterni. L'unica affermazione che fa riguardo alla virgola rispetto a INNER JOIN ON, ri ottimizzazione, è errata.
philipxy,

17

La sintassi JOIN mantiene le condizioni vicino alla tabella a cui si applicano. Ciò è particolarmente utile quando si uniscono un numero elevato di tabelle.

A proposito, puoi fare un join esterno anche con la prima sintassi:

WHERE a.x = b.x(+)

O

WHERE a.x *= b.x

O

WHERE a.x = b.x or a.x not in (select x from b)

2
La sintassi * = è deprecata in MS SQL Server e per buoni motivi: non solo rende più difficile la lettura, ma non fa ciò che la gente pensa di fare e NON è la stessa cosa di un JOIN SINISTRO simile. La sintassi (+) non mi è familiare; quale implementazione SQL fa?
Euro Micelli,

2
L'altra sintassi è utilizzata almeno da Oracle.
Lasse V. Karlsen,

4
Non usare mai la sintassi di SQL Server * =, NON fornirà risultati coerenti in quanto talvolta interpreterà come un cross join non un join sinistro. Questo vale anche fino a SQL Server 2000. Se si utilizza un codice che lo utilizza, è necessario correggere.
HLGEM,

12

Il primo modo è lo standard precedente. Il secondo metodo è stato introdotto in SQL-92, http://en.wikipedia.org/wiki/SQL . Lo standard completo può essere visualizzato su http://www.contrib.andrew.cmu.edu/~shadow/sql/sql1992.txt .

Ci sono voluti molti anni prima che le società di database adottassero lo standard SQL-92.

Quindi il motivo per cui si preferisce il secondo metodo, è lo standard SQL secondo il comitato degli standard ANSI e ISO.


,è ancora standard. ondovevano essere introdotti solo per outer joinuna volta sono state introdotte anche le selezioni secondarie.
Philipxy,

12

Fondamentalmente, quando la tua clausola FROM elenca le tabelle in questo modo:

SELECT * FROM
  tableA, tableB, tableC

il risultato è un prodotto incrociato di tutte le righe nelle tabelle A, B, C. Quindi applichi la restrizione WHERE tableA.id = tableB.a_idche eliminerà un numero enorme di righe, quindi ulteriormente ... AND tableB.id = tableC.b_ide dovresti quindi ottenere solo quelle righe che ti interessano davvero nel.

I DBMS sanno come ottimizzare questo SQL in modo che la differenza di prestazioni nella scrittura di questo tramite JOIN sia trascurabile (se presente). L'uso della notazione JOIN rende l'istruzione SQL più leggibile (IMHO, non usare i join trasforma l'istruzione in un pasticcio). Utilizzando il prodotto incrociato, è necessario fornire i criteri di join nella clausola WHERE, e questo è il problema con la notazione. Stai affollando la tua clausola WHERE con cose del genere

    tableA.id = tableB.a_id 
AND tableB.id = tableC.b_id 

che viene utilizzato solo per limitare il prodotto incrociato. La clausola WHERE deve contenere solo RESTRIZIONI al gruppo di risultati. Se mescoli i criteri di join della tabella con le restrizioni del gruppo di risultati, tu (e altri) troverete più difficile leggere la vostra query. Dovresti assolutamente usare JOINs e mantenere la clausola FROM una clausola FROM e la clausola WHERE una clausola WHERE.


10

Il secondo è preferito perché è molto meno probabile che si traduca in un cross join accidentale dimenticando di inserire la clausola where. Un join senza clausola on non supererà il controllo della sintassi, un join in vecchio stile con nessuna clausola where non avrà esito negativo, eseguirà un cross join.

Inoltre, quando in seguito è necessario un join sinistro, è utile per la manutenzione che siano tutti nella stessa struttura. E la vecchia sintassi è obsoleta dal 1992, è ormai passato il tempo di smettere di usarla.

Inoltre ho scoperto che molte persone che usano esclusivamente la prima sintassi non capiscono davvero i join e la comprensione dei join è fondamentale per ottenere risultati corretti durante le query.


6

Penso che ci siano alcuni buoni motivi in ​​questa pagina per adottare il secondo metodo che utilizza JOIN espliciti. Il clincher però è che quando i criteri JOIN vengono rimossi dalla clausola WHERE diventa molto più facile vedere i restanti criteri di selezione nella clausola WHERE.

In istruzioni SELECT davvero complesse diventa molto più facile per un lettore capire cosa sta succedendo.


5

La SELECT * FROM table1, table2, ...sintassi è ok per un paio di tabelle, ma diventa esponenzialmente ( non necessariamente un'affermazione matematicamente accurata ) sempre più difficile da leggere all'aumentare del numero di tabelle.

La sintassi JOIN è più difficile da scrivere (all'inizio), ma rende esplicito quali criteri influenzano quali tabelle. Questo rende molto più difficile fare un errore.

Inoltre, se tutti i join sono INTERNI, entrambe le versioni sono equivalenti. Tuttavia, nel momento in cui hai un OUTER aderire ovunque nell'affermazione, le cose diventano molto più complicate ed è praticamente garanzia che ciò che scrivi non interrogherà ciò che pensi di aver scritto.


2

Quando è necessario un join esterno, la seconda sintassi non è sempre richiesta:

Oracolo:

SELECT a.foo, b.foo
  FROM a, b
 WHERE a.x = b.x(+)

MSSQLServer (anche se è stato deprecato nella versione 2000) / Sybase:

SELECT a.foo, b.foo
  FROM a, b
 WHERE a.x *= b.x

Ma tornando alla tua domanda. Io non conosco la risposta, ma è probabilmente legata al fatto che una join è più naturale (sintatticamente, almeno) che l'aggiunta di un'espressione a una in cui la clausola quando si sta facendo esattamente questo: l'adesione .


Il server SQL ha deprecato la sintassi del join sinistro e anche in SQL Server 2000 non fornirà costantemente risultati corretti (a volte esegue un join incrociato anziché un join sinistro) e non dovrebbe mai essere utilizzato in SQL Server.
HLGEM,

@HLGEM: grazie per le informazioni. Ho intenzione di AGGIORNARE il mio post per riflettere ciò che stai dicendo.
Pablo Santa Cruz,

0

Ho sentito molte persone lamentarsi che il primo è troppo difficile da capire e che non è chiaro. Non vedo alcun problema, ma dopo aver avuto quella discussione, uso la seconda anche su INNER JOINS per chiarezza.


1
Sono cresciuto con l'abitudine di non usare la sintassi JOIN e di farlo nel primo modo. Devo ammettere che spesso sono ancora bloccato nell'abitudine solo perché penso che il mio cervello sia stato condizionato a seguire quella logica, dove a volte la sintassi del join mi sembra difficile da pensare.
TheTXI

3
Mi è stato insegnato anche in questo modo. Ho cambiato il mio stile di programmazione, perché la gente lo guardava e non riconosceva facilmente cosa stava succedendo. Poiché non vi è alcuna differenza logica e non riesco a trovare alcun motivo per scegliere la prima rispetto alla seconda, ho pensato che avrei dovuto adattarmi per rendere il codice più chiaro per aiutare gli altri a capire ciò che scrivo.
kemiller2002,

0

Per il database, finiscono per essere gli stessi. Per te, tuttavia, dovrai utilizzare quella seconda sintassi in alcune situazioni. Per motivi di modifica delle query che finiscono per doverlo usare (scoprendo che avevi bisogno di un join sinistro in cui avevi un join diretto), e per coerenza, modellerei solo sul secondo metodo. Semplifica la lettura delle query.


0

Bene, la prima e la seconda query possono produrre risultati diversi perché un JOIN SINISTRA include tutti i record della prima tabella, anche se non ci sono record corrispondenti nella tabella giusta.

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.