In questo articolo vedremo come scrivere una JOIN con il linguaggio SQL per collegare i dati presenti in più tabelle di un database relazionale. Utilizzare correttamente l’operatore di JOIN in SQL è un aspetto importantissimo che chiunque si avvicini al mondo delle query deve imparare a padroneggiare.
Join SQL: la sintassi
Nella clausola SELECT possiamo scrivere più colonne separandole con una virgola. Di conseguenza, se abbiamo bisogno nella stessa query di più tabelle, ad esempio Clienti e Conti), un tentativo naturale sarebbe quello di scriverle nella FROM e separarle con una virgola (SPOILER: NON FATELO!)
SELECT *
FROM Clienti, Conti;
In questo modo però otterremmo tutte le combinazioni possibili tra le righe delle due tabelle. Se ad esempio la tabella Clienti ha diecimila righe e la tabella Conti ne ha mille, la query precedente restituirà 10000*1000 = 10000000 righe! Sì, avete letto bene, dieci milioni. Avete visto come è semplice intasare un database di produzione?
Tuttavia la maggior parte di queste combinazioni saranno errate dal punto di vista logico. Ci aspettiamo infatti che all’interno delle due tabelle Conti e Clienti sia presente una colonna comune IdCliente. In generale non basta la presenza di una colonna con lo stesso nome, quello che veramente conta è che:
- da un lato, nella tabella dei Clienti, la colonna IdCliente deve identificare univocamente ogni riga (deve essere cioè una candidabile chiave primaria)
- d’altro lato, nella tabella Conti, la colonna IdCliente deve contenere per l’appunto il cliente di riferimento (deve essere cioè una candidabile chiave esterna)
Se queste due condizioni sono verificate, a prescindere dal fatto che il nome delle due colonne sia esattamente lo stesso, potrò considerare di tutte le combinazioni ottenute con la query precedente solamente quelle dove i dati delle tue colonne combaciano. Arriviamo dunque a questa query:
SELECT *
FROM Clienti, Conti
WHERE Clienti.IdCliente = Conti.IdCliente;
Per quanto corretta e ancora utilizzata da molti programmatori, questa modalità di combinare i dati tra tabelle è stata superata da una sintassi più moderna, che presenta numerosi vantaggi in termini di leggibilità e manutenibilità. Essa fa uso delle istruzioni JOIN e ON e ci permette di scrivere una query equivalente a quella precedente in questo modo:
SELECT *
FROM Clienti
INNER JOIN Conti
ON Clienti.IdCliente = Conti.IdCliente;
In questa query abbiamo utilizzato una INNER JOIN, ma vedremo che non sarà l’unica tipologia possibile.
Differenze tra le diverse tipologie di JOIN
Oltre a INNER e LEFT, esistono ulteriori tipologie di Join. Analizziamo quattro query in cui l’unica differenza è “l’aggettivo” che precede l’operatore JOIN:
SELECT *
FROM Fatture
INNER/LEFT/RIGHT/FULL
JOIN Fornitori
ON Fatture.IdFornitore = Fornitori.IdFornitore;
- Con la Inner Join mostreremo solo le combinazioni valide, equivalentemente alla sintassi con la virgola e la condizione WHERE.
- Con la Left Join mostreremo le combinazioni valide e le fatture senza fornitori associati.
- Con la Right Join mostreremo le combinazioni valide e in più fornitori senza fatture associate.
- Con la Full Join mostreremo le combinazioni valide, più le fatture senza fornitori associati, più i fornitori senza fatture associate.
Un’ultima tipologia di JOIN è la Cross Join che non richiede l’operatore ON. Essa mostra tutte le possibili combinazioni ed è dunque equivalente a separare le due tabella con la virgola nella FROM, senza inserire nessuna condizione nella WHERE.
Curiosità: sapevi che su MySQL non esiste la full join? Puoi approfondire in questo link https://www.yimp.it/full-join-mysql/ come arrivare a un risultato analogo.
Alleggeriamo la sintassi
Nella SELECT possiamo utilizzare l’AS per rinominare la colonna nell’output della query. Analogamente nella FROM possiamo usare l’AS per rinominare le tabelle all’interno della query e alleggerire così la sintassi. Ad esempio nel caso precedente potremmo scrivere
SELECT
Fa.*,
Fo.Denominazione
FROM Fatture AS Fa
INNER JOIN Fornitori AS Fo
ON Fa.IdFornitore = Fo.IdFornitore;
Vi consiglio di utilizzare una rinomina breve e che faccia riferimento alla tabella originale. In teoria è obbligatorio far precedere il nome della colonna dal riferimento alla tabella solo nel caso in cui la colonna sia ripetuta identicamente in più di una tabella. Tuttavia il mio consiglio è di specificare la tabella in qualsiasi caso. Con poco sforzo in più, otterrete una query molto più leggibile e facile da rianalizzare in futuro. Puoi leggere qui altre buone pratiche di sviluppo SQL.
Perché scegliere la sintassi con la JOIN esplicita
Ti riporto alcuni motivi per i quali preferisco esplicitare l’operatore JOIN e usare la condizione ON, invece di usare la virgola nella FROM e aggiungere una condizione nella WHERE
- leggibilità: ricordiamo che l’SQL è un linguaggio english-Like, la query deve riflettere il modo di esprimersi nel nostro linguaggio naturale. Negli esempi di prima l’obiettivo è collegare le tabelle utilizzando la colonna in comune IdCliente. Per far questo la sintassi con la JOIN esplicita è sicuramente più chiara, basta pensare alla traduzione in italiano della parola JOIN. In questo modo teniamo ben distinta la porzione di codice che esplica il collegamento tra le tabelle, da quella dove impostiamo altri filtri sui dati (come ad esempio su un’ulteriore colonna come la Regione di Residenza). Senza la JOIN tale distinzione risulta più difficile. Pensate a cosa succede quando la query è più complessa e sono presenti numerosissime join e svariati filtri;
- manutenibilità: supponiamo di dover trasformare l’INNER JOIN in una LEFT JOIN. Se abbiamo già esplicitato la JOIN ci basterà modificare una parola! Altrimenti su molti database dovremo riscrivere completamente la query;
- facilità di scrittura: supponiamo di voler eliminare temporaneamente la JOIN. Se ho utilizzato la WHERE per scrivere la condizione di collegamento l’operazione risulterà sicuramente più complessa e comprende il rischio di commentare per errore anche un filtro su un’altra colonna.
Gestire la presenza di null nelle Join in SQL
La presenza di NULL complica spesso la scrittura di una query soprattutto quando la condizione di JOIN coinvolge più colonne. Supponiamo ad esempio che la tabella dei clienti e dei conti devono essere collegate secondo questa specifica: devono avere lo stesso codice_conto e, se presente, lo stesso codice_valuta. Chi lavora in ambito banking avrà sicuramente riconosciuto questa specifica collegata alla presenza di conti in più valute.
Occorre fare molta attenzione perché la seguente query, all’apparenza corretta, esclude tutti quei conti che hanno NULL nella colonna codice_valuta
SELECT *
FROM DimConto AS c
INNER JOIN SnapshotConto AS s
ON c.codice_conto = s.codice_conto
AND c.codice_valuta = s.codice_valuta;
Per risolvere questa fastidiosa situazione, vi lascio un link a un fantastico articolo di Itzik Ben Gan legato alla gestione del NULL. Vi riporto la soluzione più “elegante”:
SELECT c.*, s.*
FROM DimConto AS c
INNER JOIN SnapshotConto AS s
ON EXISTS(
SELECT c.codice_conto,
c.codice_valuta
INTERSECT
ELECT s.codice_conto,
s.codice_valuta);
Continua a imparare
- Segui la lezione successiva sulla Group by SQL
- Torna all’indice delle lezioni
- Visita la pagina del mio videocorso SQL