JOIN esterni in SQL

Nella lezione precedente abbiamo visto come utilizzare INNER JOIN per collegare più tabelle e ottenere soltanto le righe che hanno una corrispondenza in entrambe le tabelle.

A volte, però, potremmo voler ottenere tutte le righe di una tabella, anche quando non esiste una riga corrispondente nell'altra. Ad esempio, in una tabella clienti con l'elenco dei clienti e una tabella ordini con gli ordini effettuati, potremmo voler visualizzare tutti i clienti, compresi quelli che non hanno ancora effettuato alcun ordine.

Per questo scopo possiamo utilizzare i JOIN esterni, in particolare LEFT JOIN e RIGHT JOIN.

Usare LEFT JOIN

Un LEFT JOIN restituisce:

  • Tutte le righe della tabella a sinistra.
  • Le informazioni corrispondenti della tabella a destra, quando presenti.
  • NULL nelle colonne della tabella destra quando non esiste alcuna corrispondenza.

Consideriamo le tabelle clienti e ordini:

Tabella clienti e ordini relazionali

Con una semplice query INNER JOIN, i clienti che non hanno effettuato ordini non vengono inclusi nel risultato.

Utilizzando LEFT JOIN:

SELECT c.nome, o.totale
FROM clienti c
LEFT JOIN ordini o
    ON c.id = o.cliente_id;

Se un cliente non ha effettuato acquisti, viene comunque incluso perché clienti è la tabella a sinistra e LEFT JOIN mantiene tutte le sue righe. Non essendoci un ordine corrispondente, le colonne provenienti da ordini assumono valore NULL.

Cambiare l'ordine delle tabelle

Se consideriamo la seguente parte di query:

FROM clienti c
LEFT JOIN ordini o
    ON c.id = o.cliente_id

La tabella a sinistra è clienti, quindi tutti i clienti vengono mantenuti. 

Se invece scriviamo:

FROM ordini o
LEFT JOIN clienti c
    ON c.id = o.cliente_id

Nella query, la tabella a sinistra diventa ordini e quindi saranno mantenuti tutti gli ordini.

Per questo motivo, quando utilizziamo LEFT JOIN, è importante prestare attenzione all'ordine delle tabelle.

Usare RIGHT JOIN

Il RIGHT JOIN funziona in modo simile al LEFT JOIN, ma mantiene tutte le righe della tabella a destra.

In questo esempio, vogliamo mantenere tutti gli ordini:

FROM ordini o
RIGHT JOIN clienti c
    ON c.id = o.cliente_id

In questo caso vengono mantenuti tutti gli ordini, anche se per qualche motivo non esiste un cliente corrispondente. Se un ordine non ha un cliente associato, le colonne provenienti da clienti avranno valore NULL.

RIGHT JOIN o LEFT JOIN

Un RIGHT JOIN può essere generalmente riscritto invertendo l'ordine delle tabelle e utilizzando un LEFT JOIN.

Prendiamo questa query come esempio:

SELECT c.nome, o.totale
FROM clienti c
RIGHT JOIN ordini o
    ON c.id = o.cliente_id;

Possiamo riscriverla ottenendo lo stesso risultato così:

SELECT c.nome, o.totale
FROM ordini o
LEFT JOIN clienti c
    ON c.id = o.cliente_id;

In entrambi i casi vengono mantenuti tutti gli ordini.

Per questo motivo, nella pratica, LEFT JOIN viene spesso preferito a RIGHT JOIN: è sufficiente invertire l'ordine delle tabelle per ottenere lo stesso risultato.

Quando usare JOIN esterni

Una delle applicazioni più utili di LEFT JOIN è trovare le righe della tabella sinistra che non hanno alcuna corrispondenza nella tabella destra.

Riprendiamo l'esempio dei clienti: vogliamo trovare tutti i clienti che non hanno effettuato nessun ordine. In questo caso, possiamo scrivere:

SELECT c.id, c.nome
FROM clienti c
LEFT JOIN ordini o
    ON c.id = o.cliente_id
WHERE o.id IS NULL;

Nell'esempio, il LEFT JOIN mantiene tutti i clienti. Per i clienti che hanno un ordine, MySQL trova una corrispondenza e assegna un valore alle colonne di ordini.

Per i clienti senza ordini, invece, le colonne di ordini vengono impostate a NULL. A questo punto, WHERE o.id IS NULL mantiene soltanto le righe in cui non è stata trovata una corrispondenza.

Prova!Completa gli spazi vuoti con il testo appropriato.
-- Mantieni tutti i clienti, anche senza ordini
SELECT c.nome, o.totale
FROM clienti c
JOIN ordini o
    ON c.id = o.cliente_id;

Prova! Scegli l'opzione corretta tra quelle elencate.
-- Mantieni tutti i prodotti,
-- anche quelli mai inclusi in una fattura
SELECT p.nome, f.numero
FROM fatture f
_____ JOIN prodotti p
    ON f.prodotto_id = p.id;