Logo NEXSYS menu
ISO 9001

Nexsys Srl è certificata
ISO 9001:2015

Microsoft Solutions Partner

Siamo Microsoft Solution Partner per il Modern Work

APPROFONDIMENTI E NEWS

SQL JOIN: come unire tabelle con INNER, LEFT, RIGHT e FULL JOIN

Le SQL JOIN sono uno dei meccanismi fondamentali dei database relazionali: permettono di combinare righe provenienti da due o più tabelle sulla base di una relazione logica, trasformando dati separati in un insieme coerente e interrogabile. In pratica, una JOIN consente di rispondere a domande che una singola tabella non può soddisfare: collegare ordini e clienti, associare prodotti e categorie, mettere in relazione utenti e ruoli oppure confrontare dati distribuiti su entità differenti.

Capire le JOIN significa quindi capire come leggere il modello relazionale. Non basta conoscere la parola chiave JOIN: è necessario distinguere il tipo di relazione che si vuole ottenere, sapere dove applicare i filtri e valutare come NULL, duplicati e cardinalità influenzano il risultato. In questa guida analizziamo le principali tipologie di SQL JOIN con esempi in sintassi Transact-SQL, mantenendo i concetti validi anche per gli altri database relazionali che implementano SQL.

Che cos’è una SQL JOIN

Una JOIN combina righe appartenenti a origini tabellari diverse quando viene soddisfatta una condizione. La condizione viene normalmente dichiarata con ON e mette in relazione colonne che rappresentano lo stesso concetto logico: per esempio CustomerID nella tabella Customers e CustomerID nella tabella Orders. La JOIN non “incolla” semplicemente due tabelle: costruisce un nuovo result set secondo regole precise.

Immaginiamo due tabelle: Customers contiene una riga per ogni cliente; Orders contiene gli ordini e registra, per ogni ordine, l’identificativo del cliente che lo ha effettuato. Interrogare solo Customers permette di conoscere i clienti, mentre interrogare solo Orders mostra gli ordini. Una JOIN permette di ottenere in un’unica query il nome del cliente, la data dell’ordine e il relativo importo.

sql join

La condizione ON e le relazioni tra chiavi

Nella progettazione relazionale la relazione è spesso rappresentata da una primary key nella tabella principale e da una foreign key nella tabella collegata. SQL, tuttavia, non richiede che ogni JOIN utilizzi formalmente una foreign key: la condizione può essere costruita anche su altre colonne compatibili. Dal punto di vista progettuale è comunque preferibile collegare attributi con significato coerente, evitando confronti ambigui o conversioni implicite che possono produrre risultati errati e peggiorare le prestazioni.

SELECT c.CustomerID, c.CompanyName, o.OrderID, o.OrderDate
FROM Customers AS c
INNER JOIN Orders AS o
    ON c.CustomerID = o.CustomerID;

INNER JOIN: restituisce solo le corrispondenze

INNER JOIN è la scelta più comune quando interessano soltanto le righe che trovano una corrispondenza in entrambe le tabelle. Se un cliente non ha ordini, quel cliente non appare nel risultato; allo stesso modo, un ordine privo di un cliente corrispondente non viene restituito. Il result set rappresenta quindi l’intersezione logica dei dati rispetto alla condizione indicata in ON.

Questo comportamento è adatto, per esempio, quando vogliamo elencare esclusivamente i clienti che hanno effettuato almeno un ordine, i dipendenti assegnati a un reparto esistente oppure i prodotti associati a una categoria valida.

SELECT c.CompanyName, o.OrderID, o.TotalAmount
FROM Customers AS c
INNER JOIN Orders AS o
    ON c.CustomerID = o.CustomerID
WHERE o.OrderDate >= '2026-01-01';

Nell’esempio la JOIN definisce la relazione tra clienti e ordini, mentre WHERE limita il risultato agli ordini dal 1° gennaio 2026. Separare mentalmente questi due compiti è importante: ON stabilisce come le origini sono collegate; WHERE stabilisce quali righe del risultato devono essere mantenute.

LEFT JOIN: mantenere tutte le righe della tabella sinistra

LEFT JOIN, chiamata anche LEFT OUTER JOIN, restituisce tutte le righe della tabella indicata a sinistra della JOIN e aggiunge i dati della tabella di destra quando esiste una corrispondenza. Se la corrispondenza non esiste, le colonne della tabella destra assumono valore NULL.

È particolarmente utile quando l’assenza di una relazione è essa stessa un’informazione. Un caso classico è cercare i clienti che non hanno mai effettuato ordini: con una LEFT JOIN possiamo mantenere tutti i clienti e poi selezionare quelli per i quali non esiste un OrderID associato.

SELECT c.CustomerID, c.CompanyName
FROM Customers AS c
LEFT JOIN Orders AS o
    ON c.CustomerID = o.CustomerID
WHERE o.OrderID IS NULL;

Questa struttura è molto diversa da una INNER JOIN: qui l’obiettivo non è trovare le corrispondenze, ma preservare l’intera popolazione della tabella principale e identificare ciò che manca nella tabella correlata.

RIGHT JOIN: il comportamento speculare

RIGHT JOIN applica lo stesso principio della LEFT JOIN ma preserva tutte le righe della tabella posta a destra. È supportata da SQL Server e da diversi motori SQL, ma nella pratica molti team preferiscono riscrivere la query invertendo l’ordine delle tabelle e usando LEFT JOIN, perché questo rende il codice più uniforme e spesso più immediato da leggere.

SELECT c.CompanyName, o.OrderID
FROM Customers AS c
RIGHT JOIN Orders AS o
    ON c.CustomerID = o.CustomerID;

Il risultato include tutti gli ordini. Se per qualche motivo un ordine non trovasse un cliente corrispondente, le colonne provenienti da Customers risulterebbero NULL. In un database con vincoli referenziali coerenti questa situazione dovrebbe essere rara, ma la semantica della RIGHT JOIN rimane utile da conoscere.

FULL OUTER JOIN: mantenere entrambe le popolazioni

FULL OUTER JOIN restituisce tutte le righe di entrambe le tabelle: le righe corrispondenti vengono combinate, mentre quelle presenti soltanto da un lato restano nel result set con NULL nelle colonne dell’altra origine. È utile in attività di riconciliazione, confronto tra dataset, verifica di migrazioni o individuazione di record presenti in un sistema ma assenti in un altro.

SELECT a.RecordID AS SourceA, b.RecordID AS SourceB
FROM SourceA AS a
FULL OUTER JOIN SourceB AS b
    ON a.RecordID = b.RecordID;

Una FULL OUTER JOIN può produrre result set molto ampi. Per questo va usata quando serve davvero preservare entrambe le popolazioni e non come sostituto generico di INNER o LEFT JOIN.

SQL JOIN – Diagramma

sql join – diagramma

 

CROSS JOIN: il prodotto cartesiano

CROSS JOIN combina ogni riga della prima tabella con ogni riga della seconda. Se la prima tabella contiene 100 righe e la seconda 50, il risultato contiene 5.000 righe. Questo comportamento corrisponde al prodotto cartesiano dell’algebra relazionale e non richiede una condizione ON.

Il prodotto cartesiano non è necessariamente un errore: può essere utile per generare tutte le combinazioni possibili tra due insiemi, per esempio calendari, scenari, matrici di configurazione o dataset di test. Diventa però pericoloso quando nasce accidentalmente da una condizione di JOIN mancante o errata, perché il numero di righe cresce molto rapidamente.

SELECT s.SizeName, c.ColorName
FROM Sizes AS s
CROSS JOIN Colors AS c;

JOIN su tre o più tabelle

Una query può contenere più JOIN. In un modello reale è normale dover attraversare diverse relazioni: ordini, clienti, dettagli ordine e prodotti possono essere combinati nella stessa SELECT. Il punto essenziale è mantenere leggibili gli alias e dichiarare una condizione ON precisa per ogni relazione.

SELECT c.CompanyName, o.OrderID, p.ProductName, d.Quantity
FROM Customers AS c
INNER JOIN Orders AS o
    ON c.CustomerID = o.CustomerID
INNER JOIN OrderDetails AS d
    ON o.OrderID = d.OrderID
INNER JOIN Products AS p
    ON d.ProductID = p.ProductID;

Quando il numero di tabelle aumenta, conviene verificare la cardinalità di ogni relazione. Una relazione uno-a-molti moltiplica correttamente le righe; se però due tabelle contengono più record per la stessa chiave e vengono collegate senza una condizione sufficiente, il risultato può contenere duplicazioni apparentemente inspiegabili.

ON e WHERE: una differenza che cambia il risultato

Con INNER JOIN molti filtri applicati in ON o in WHERE possono produrre lo stesso insieme finale. Con le OUTER JOIN, invece, la posizione del filtro può cambiare radicalmente la semantica. Un filtro sulla tabella opzionale inserito in WHERE può eliminare le righe con NULL e trasformare di fatto una LEFT JOIN in un comportamento simile a una INNER JOIN.

Per evitare errori, conviene adottare una regola semplice: usare ON per descrivere la relazione e gli eventuali criteri che determinano la corrispondenza; usare WHERE per filtrare il result set finale, verificando esplicitamente l’effetto sui valori NULL.

 

dp-080 transact sql

NULL, duplicati e cardinalità

Tre problemi ricorrenti nelle query con JOIN sono la gestione dei NULL, la comparsa di duplicati e una stima errata della cardinalità. NULL non è un valore normale e non deve essere confrontato con = NULL: per verificarlo si usano IS NULL e IS NOT NULL. I duplicati, invece, spesso non sono duplicati reali ma la conseguenza di una relazione uno-a-molti o molti-a-molti correttamente espansa dalla JOIN.

Usare DISTINCT per “nascondere” il problema senza comprenderne la causa è una pratica fragile. Prima di aggiungerlo è opportuno verificare chiavi, cardinalità e condizione ON. Se il risultato deve avere una riga per cliente ma un cliente ha dieci ordini, la query deve dichiarare come aggregare o selezionare quegli ordini; la JOIN non può dedurlo automaticamente.

Prestazioni: cosa controllare quando una JOIN diventa lenta

Le prestazioni dipendono dalla dimensione delle tabelle, dalla selettività dei filtri, dagli indici disponibili, dai tipi di dati e dal piano di esecuzione scelto dal database. In SQL Server è utile verificare che le colonne usate frequentemente per collegare tabelle dispongano di indici coerenti con il workload, evitando conversioni implicite tra tipi incompatibili e filtri applicati troppo tardi su dataset molto ampi.

Non esiste però una regola per cui “più indici = JOIN più veloci”. Gli indici hanno un costo in scrittura e manutenzione e devono essere progettati sulla base delle query reali. Per analisi strutturate di Transact-SQL, piani di esecuzione e interrogazioni su più tabelle, il percorso formativo dedicato è il corso DP-080 Querying Data with Transact-SQL

Errori comuni nelle SQL JOIN

  • Omettere o rendere troppo generica la condizione ON, generando combinazioni non previste.
  • Usare INNER JOIN quando servono anche le righe senza corrispondenza.
  • Applicare in WHERE un filtro sulla tabella destra di una LEFT JOIN senza considerare i NULL.
  • Collegare colonne con tipi differenti, costringendo il motore a conversioni implicite.
  • Usare DISTINCT per eliminare righe senza aver prima analizzato la cardinalità.
  • Selezionare SELECT * in query complesse, trasferendo colonne non necessarie e rendendo meno leggibile il risultato.

Vuoi approfondire Transact-SQL con esercitazioni guidate?

Il percorso Nexsys copre SELECT, JOIN, filtri, aggregazioni e interrogazioni su più tabelle. Scopri il corso DP-080

 

SQL JOIN e database diversi

I concetti di INNER, LEFT, RIGHT e CROSS JOIN sono condivisi da molti database relazionali, anche se esistono differenze di sintassi e funzionalità. SQL Server utilizza Transact-SQL; Oracle, PostgreSQL e altri motori implementano proprie estensioni. Per chi lavora specificamente su Oracle Database, Nexsys propone anche il corso Oracle SQL, separato dall’approfondimento Transact-SQL.

La competenza realmente trasferibile non consiste nel memorizzare una singola sintassi, ma nel riconoscere la relazione tra i dati, scegliere il tipo di JOIN corretto e controllare la cardinalità del risultato. Una volta compreso questo modello, la sintassi del singolo database diventa molto più semplice da applicare.

FAQ sulle SQL JOIN

Che differenza c’è tra INNER JOIN e LEFT JOIN?

INNER JOIN restituisce soltanto le righe che hanno una corrispondenza in entrambe le tabelle. LEFT JOIN mantiene invece tutte le righe della tabella a sinistra e inserisce NULL per i dati della tabella destra quando non esiste una corrispondenza.

Quando usare FULL OUTER JOIN?

FULL OUTER JOIN è utile quando occorre mantenere tutte le righe di entrambe le origini e individuare sia le corrispondenze sia i record presenti soltanto da un lato, ad esempio in attività di riconciliazione o confronto tra dataset.

Una JOIN richiede sempre una foreign key?

No. SQL può eseguire una JOIN su colonne compatibili anche senza un vincolo di foreign key. Dal punto di vista progettuale è però preferibile collegare attributi che rappresentano realmente la stessa relazione logica.

Perché una JOIN produce righe duplicate?

Spesso non si tratta di duplicati reali ma dell’effetto della cardinalità uno-a-molti o molti-a-molti. Prima di usare DISTINCT bisogna controllare la condizione ON e capire quante righe correlate esistono per ogni chiave.

Qual è il corso Nexsys più adatto per approfondire le JOIN in SQL Server?

Il corso DP-080 Querying Data with Transact-SQL è il percorso più coerente per approfondire interrogazioni, filtri, JOIN e gestione dei dati in ambiente Microsoft SQL Server.