🗃️ Operatori Relazionali

Guida Completa con Esempi in MariaDB/MySQL

🛠️ Setup: Le Nostre Tabelle di Esempio

📋 Scenario

Useremo un semplice database con Studenti e Corsi per tutti gli esempi.

👤 STUDENTI
id nome città età
1 Mario Roma 22
2 Laura Milano 20
3 Luca Roma 23
4 Anna Napoli 21
📚 CORSI
id_corso nome_corso docente
101 Database Rossi
102 Programmazione Bianchi
103 Reti Verdi
104 Yoga Aldi
📝 ESAME
id_studente id_corso voto
1 101 28
1 102 30
2 101 25
3 103 27
📜 SQL - Creazione Tabelle
-- Creazione del database
CREATE DATABASE IF NOT EXISTS universita;
USE universita;

-- Tabella STUDENTI
CREATE TABLE studenti (
    id INT PRIMARY KEY,
    nome VARCHAR(50),
    citta VARCHAR(50),
    eta INT
);

INSERT INTO studenti VALUES
    (1, 'Mario', 'Roma', 22),
    (2, 'Laura', 'Milano', 20),
    (3, 'Luca', 'Roma', 23),
    (4, 'Anna', 'Napoli', 21);

-- Tabella CORSI
CREATE TABLE corsi (
    id_corso INT PRIMARY KEY,
    nome_corso VARCHAR(50),
    docente VARCHAR(50)
);

INSERT INTO corsi VALUES
    (101, 'Database', 'Rossi'),
    (102, 'Programmazione', 'Bianchi'),
    (103, 'Reti', 'Verdi');

-- Tabella ESAME (tabella di collegamento)
CREATE TABLE ESAME (
    id_studente INT,
    id_corso INT,
    voto INT,
    PRIMARY KEY (id_studente, id_corso),
    FOREIGN KEY (id_studente) REFERENCES studenti(id),
    FOREIGN KEY (id_corso) REFERENCES corsi(id_corso)
);

INSERT INTO ESAME VALUES
    (1, 101, 28),
    (1, 102, 30),
    (2, 101, 25),
    (3, 103, 27);

📌 Operatori Unari (su una sola tabella)

SELEZIONE (σ - sigma)

📖 Definizione

La selezione filtra le righe di una tabella in base a una condizione. Restituisce solo le tuple (righe) che soddisfano una condizione.

σcondizione(Tabella)

🎯 Obiettivo: Trovare tutti gli studenti che vivono a Roma

STUDENTI (originale)
id nome città età
1 Mario Roma 22
2 Laura Milano 20
3 Luca Roma 23
4 Anna Napoli 21
✅ RISULTATO
id nome città età
1 Mario Roma 22
3 Luca Roma 23
📜 MariaDB SQL
-- Selezione: tutti gli studenti di Roma
SELECT *
FROM studenti
WHERE citta = 'Roma';

-- Selezione con più condizioni (AND): tutti gli studenti che vivono a Roma e hanno più di 21 anni
SELECT *
FROM studenti
WHERE citta = 'Roma' AND eta > 21;

-- Selezione con OR: tutti gli studenti che vivono a Roma o a Milano
SELECT *
FROM studenti
WHERE citta = 'Roma' OR citta = 'Milano';

-- Selezione con IN (più elegante): tutti gli studenti che vivono a Roma o a Milano
SELECT *
FROM studenti
WHERE citta IN ('Roma', 'Milano');
💡 Ricorda

La selezione opera sulle RIGHE (orizzontalmente). Il numero di colonne resta invariato!

PROIEZIONE (π - pi)

📖 Definizione

La proiezione seleziona solo alcune colonne dalla tabella, eliminando le altre. .

πcolonna1, colonna2(Tabella)

🎯 Obiettivo: Ottenere solo nome e città degli studenti

STUDENTI (originale)
nome città id età
Mario Roma 1 22
Laura Milano 2 20
Luca Roma 3 23
Anna Napoli 4 21
✅ RISULTATO
nome città
Mario Roma
Laura Milano
Luca Roma
Anna Napoli
📜 MariaDB SQL
-- L'obiettivo è mostrare solo nome e città di tutti gli studenti
-- Nascondiamo id ed età perché non servono per questa visualizzazione
SELECT nome, citta
FROM studenti;

-- Per eliminare duplicati (come in algebra relazionale)
SELECT DISTINCT citta
FROM studenti;
-- Risultato: Roma, Milano, Napoli (3 righe, non 4)

-- Combinazione Selezione + Proiezione
-- σ città='Roma' e poi π nome
SELECT nome
FROM studenti
WHERE citta = 'Roma';
💡 Ricorda

La proiezione opera sulle COLONNE (verticalmente). Il numero di righe può diminuire se si usa DISTINCT!

🔗 Prodotto Cartesiano e JOIN

PRODOTTO CARTESIANO (×)

📖 Definizione

Il prodotto cartesiano combina ogni riga della prima tabella con ogni riga della seconda tabella. Genera tutte le combinazioni possibili.

Studenti × ESAMI
STUDENTI
idnome
1Mario
2Laura
×
ESAMI
id_studentevoto
128
230
RISULTATO PRODOTTO CARTESIANO (2 × 2 = 4 righe)
id nome id_studente voto
1Mario128
1Mario230
2Laura128
2Laura230
⚠️ La "Logica" del Computer

Il computer non sa quali righe appartengono a chi. Combina ogni studente con ogni esame esistente nel database.
Sarà poi compito del JOIN filtrare solo quelle dove gli ID corrispondono.

🔍 Analisi: quali righe hanno senso logico?

Riga Studente (id) Esame (id_studente) Condizione: id = id_studente?
1 Mario (1) 1 → voto 28 ✅ 1 = 1 → MANTIENI
2 Mario (1) 2 → voto 30 ⛔ 1 ≠ 2 → SCARTA : IL VOTO NON E' DI MARIO
3 Laura (2) 1 → voto 28 ⛔ 2 ≠ 1 → SCARTA:IL VOTO NON E' DI LAURA
4 Laura (2) 2 → voto 30 ✅ 2 = 2 → MANTIENI

Il JOIN non è altro che questo Prodotto Cartesiano a cui applichiamo una condizione.
Questo elimina le righe rosse (SCARTA) e tiene solo quelle verdi (MANTIENI).

📜 MariaDB SQL
-- Prodotto Cartesiano Puro (tutte le combinazioni)
SELECT * 
FROM studenti 
CROSS JOIN esami;

INNER JOIN (Theta Join / Equi-Join) ⋈

📖 Definizione

L'INNER JOIN è un prodotto cartesiano filtrato da una condizione. Restituisce solo le righe dove la condizione specificata nel join è soddisfatta

💡 Nota: In SQL, JOIN e INNER JOIN sono equivalenti. La parola INNER è opzionale: scrivere" FROM A JOIN B ON ... "è identico a "FROM A INNER JOIN B ON ...". Entrambi restituiscono solo le righe che hanno corrispondenza in entrambe le tabelle.

Tabella1 ⋈condizione Tabella2

📌 Esempio 1: Join Base (Studenti → Esami)

🎯 Obiettivo: Collegare studenti ai loro esami

STUDENTI
idnome
1Mario
2Laura
3Luca
4Anna
id = id_studente
ESAMI
id_studenteid_corsovoto
110128
110230
210125
310327
✅ RISULTATO INNER JOIN
idnomeid_studenteid_corsovoto
1Mario110128
1Mario110230
2Laura210125
3Luca310327
👀 Nota

Anna (id=4) non appare nel risultato perché non ha esami (nessun match nella tabella ESAMI)!
Mario appare 2 volte perché ha sostenuto 2 esami.

📜 MariaDB SQL
-- INNER JOIN: mostra solo studenti che hanno almeno un esame
-- L'obiettivo è abbinare ogni studente ai suoi esami (se esistono)
SELECT *
FROM studenti s
INNER JOIN esami e ON s.id = e.id_studente;

-- Equivalente: JOIN senza INNER produce lo stesso risultato
-- Mostra gli stessi studenti con i loro esami
SELECT *
FROM studenti s
JOIN esami e ON s.id = e.id_studente;

📌 Esempio 2: Join tra 3 Tabelle (Studenti → Esami → Corsi)

🎯 Obiettivo: visualizzare nome studente, nome corso e voto

STUDENTI
idnome
1Mario
2Laura
ESAMI
id_studenteid_corsovoto
110128
110230
210125
CORSI
idnome_corsocrediti
101Matematica6
102Fisica9
103Chimica6
✅ RISULTATO: Studenti + Esami + Corsi
nomenome_corsocreditivoto
MarioMatematica628
MarioFisica930
LauraMatematica625
📜 MariaDB SQL
-- L'obiettivo è ottenere un report completo: nome studente, corso frequentato e voto ricevuto
-- Servono 3 tabelle perché ogni tabella contiene informazioni diverse che vanno combinate
SELECT 
    s.nome,
    c.nome_corso,
    c.crediti,
    e.voto
FROM studenti s
JOIN esami e ON s.id = e.id_studente
JOIN corsi c ON e.id_corso = c.id;

📌 Esempio 3: E-Commerce (Clienti → Ordini → Prodotti)

🎯 Obiettivo: visualizzare cosa ha comprato ogni cliente

CLIENTI
idnomecittà
1MarcoRoma
2GiuliaMilano
ORDINI
idid_clienteid_prodottoquantità
11102
21201
32103
PRODOTTI
idnomeprezzo
10Laptop800
20Mouse25
✅ RISULTATO: Dettaglio Ordini
clientecittàprodottoquantitàprezzo_unittotale
MarcoRomaLaptop28001600
MarcoRomaMouse12525
GiuliaMilanoLaptop38002400
📜 MariaDB SQL
-- L'obiettivo è mostrare: chi ha comprato cosa, quanto, e il totale speso (quantità × prezzo)
SELECT 
    c.nome AS cliente,
    c.città,
    p.nome AS prodotto,
    o.quantità,
    p.prezzo AS prezzo_unit,
    (o.quantità * p.prezzo) AS totale
FROM clienti c
JOIN ordini o ON c.id = o.id_cliente
JOIN prodotti p ON o.id_prodotto = p.id;

📌 Esempio 4: Join con Condizioni Multiple

🎯 Obiettivo: Trovare i turni di lavoro per ogni dipendente, abbinando sia il reparto che la sede (per evitare errori)

DIPENDENTI
idnomerepartosede
1AnnaVenditeMilano
2MarcoVenditeRoma
3SaraITMilano
TURNI
repartosedegiornoorario
VenditeMilanoLunedì9-17
VenditeRomaMartedì10-18
ITMilanoLunedì10-18
✅ RISULTATO: Turni per Dipendente (corretto)
nomerepartosedegiornoorario
AnnaVenditeMilanoLunedì9-17
MarcoVenditeRomaMartedì10-18
SaraITMilanoLunedì10-18
📜 MariaDB SQL
-- L'obiettivo è trovare il turno esatto per ogni dipendente
-- Serve il match su DUE colonne per evitare errori 
--(es. non assegnare il turno di Milano a chi lavora a Roma) SELECT d.nome, d.reparto, d.sede, t.giorno, t.orario FROM dipendenti d JOIN turni t ON d.reparto = t.reparto AND d.sede = t.sede; -- Cosa succede se usi solo UNA colonna? (ERRATO!) SELECT d.nome, d.reparto, t.giorno, t.orario FROM dipendenti d JOIN turni t ON d.reparto = t.reparto; -- Risultato SBAGLIATO: Anna (Vendite-Milano) avrebbe anche il turno di Roma! -- Join con condizioni multiple (AND) - Esempio generico SELECT * FROM tabella1 t1 JOIN tabella2 t2 ON t1.colonna_a = t2.colonna_a AND t1.colonna_b = t2.colonna_b;

📌 Esempio 5: Join con (Condizione ≠ Uguaglianza) Theta Join è il concetto più generale di JOIN: unisce due tabelle basandosi su qualsiasi condizione logica (predicato), non necessariamente un'uguaglianza. Operatori ammessi: =, <, >, <=, >=, <>, !=, LIKE, BETWEEN, ecc. L'equijoin è il theta Join in cui la condizione è espressa con "="

🎯 Obiettivo: Trovare prodotti nel budget del cliente

CLIENTI
idnomebudget
1Marco500
2Giulia1000
budget >= prezzo
PRODOTTI
idnomeprezzo
1Mouse25
2Tastiera80
3Monitor300
4Laptop800
✅ RISULTATO: Prodotti Acquistabili
clientebudgetprodottoprezzo
Marco500Mouse25
Marco500Tastiera80
Marco500Monitor300
Giulia1000Mouse25
Giulia1000Tastiera80
Giulia1000Monitor300
Giulia1000Laptop800
👀 Nota

Marco può acquistare 3 prodotti (budget 500€), Giulia può acquistarne 4 (budget 1000€).

📜 MariaDB SQL
-- Theta Join con condizione >=: l'obiettivo è trovare prodotti che il cliente può permettersi
-- Uniamo clienti e prodotti dove il budget del cliente è maggiore o uguale al prezzo del prodotto
SELECT 
    c.nome AS cliente,
    c.budget,
    p.nome AS prodotto,
    p.prezzo
FROM clienti c
JOIN prodotti p ON c.budget >= p.prezzo
ORDER BY c.nome, p.prezzo;

-- Altro esempio di Theta join: trovare prodotti in una fascia di prezzo
SELECT *
FROM fasce_prezzo f
JOIN prodotti p 
    ON p.prezzo >= f.min_prezzo 
    AND p.prezzo <= f.max_prezzo;

📌 Esempio 6: Join + Filtro WHERE

🎯 Obiettivo: Trovare esami con voto alto di studenti specifici

⚠️ Differenza ON vs WHERE

ON: condizione per collegare le tabelle (relazione)
WHERE: filtro sui risultati (selezione)

📜 MariaDB SQL
-- Esami con voto >= 28 di studenti il cui nome inizia per 'M'
SELECT 
    s.nome,
    e.corso,
    e.voto
FROM studenti s
JOIN esami e ON s.id = e.id_studente  -- fin quì esami con voto di tutti gli studenti
WHERE s.nome LIKE 'M%'           -- filtro studente
  AND e.voto >= 28;                -- filtro voto

-- Ordini del 2024 con importo > 100€
SELECT 
    c.nome,
    o.data_ordine,
    o.importo
FROM clienti c
JOIN ordini o ON c.id = o.id_cliente
WHERE YEAR(o.data_ordine) = 2024
  AND o.importo > 100
ORDER BY o.data_ordine DESC;

LEFT JOIN (Left Join) ⟕

📖 Definizione

Il LEFT JOIN restituisce tutte le righe della tabella di SINISTRA, più le corrispondenze dalla tabella di destra. Se non c'è corrispondenza, le colonne della destra sono NULL.

Studenti ⟕ Iscrizioni

🎯 Obiettivo: Tutti gli studenti CON le loro iscrizioni (anche chi non è iscritto)

STUDENTI (Tabella Sinistra)
idnome
1Mario
2Laura
3Luca
4Anna
ISCRIZIONI (Tabella Destra)
id_studenteid_corsovoto
110128
110230
210125
310327
✅ RISULTATO LEFT JOIN
idnomeid_corsovoto
1Mario10128
1Mario10230
2Laura10125
3Luca10327
4AnnaNULLNULL
👀 Nota

Anna appare! Anche se non ha iscrizioni, viene inclusa con NULL nelle colonne di iscrizioni perché è nella tabella di SINISTRA.
Tutte le iscrizioni esistenti sono abbinate agli studenti corrispondenti.

📜 MariaDB SQL
-- LEFT JOIN: mostra TUTTI gli studenti, anche quelli senza esami
-- L'obiettivo è visualizzare chi ha esami e chi no (NULL = nessun esame)
SELECT s.id, s.nome, i.id_corso, i.voto
FROM studenti s
LEFT JOIN iscrizioni i ON s.id = i.id_studente;

-- Trovare studenti SENZA esami (filtro su NULL)
-- L'obiettivo è trovare chi NON ha sostenuto alcun esame
SELECT s.*
FROM studenti s
LEFT JOIN iscrizioni i ON s.id = i.id_studente
WHERE i.id_studente IS NULL;
-- Risultato: solo Anna (non ha esami)

RIGHT JOIN (Right Join) ⟖

📖 Definizione

Il RIGHT JOIN è l'opposto del LEFT: restituisce tutte le righe della tabella di DESTRA, più le corrispondenze dalla sinistra. Se non c'è corrispondenza, le colonne della sinistra sono NULL.

Iscrizioni ⟖ Corsi

🎯 Obiettivo: Voglio l'elenco di TUTTI i corsi offerti dall'università e visualizzare chi è iscritto. Se un corso non ha iscritti (es. Yoga), voglio vederlo lo stesso!

ISCRIZIONI (Tabella Sinistra)
id_studenteid_corsovoto
110128
110230
210125
310327
CORSI (Tabella Destra)
id_corsonome_corso
101Database
102Programmazione
103Reti
104Yoga
✅ RISULTATO RIGHT JOIN
nome_corsoid_studentevoto
Database128
Database225
Programmazione130
Reti327
YogaNULLNULL
💡 Analisi

Il corso di Yoga appare nel risultato (con NULL a sinistra) perché abbiamo chiesto di preservare la tabella di DESTRA (Corsi), anche se non c'erano match nella tabella Iscrizioni.

📜 MariaDB SQL
-- RIGHT JOIN: mostra TUTTI i corsi, anche quelli senza studenti iscritti
-- L'obiettivo è visualizzare quali corsi hanno iscritti e quali no (NULL = nessuno iscritto)
SELECT c.nome_corso, i.id_studente, i.voto
FROM iscrizioni i
RIGHT JOIN corsi c ON i.id_corso = c.id_corso;

-- Risultato: Yoga appare con NULL perché nessuno è iscritto
-- Database      | 1    | 28
-- Database      | 2    | 25
-- Programmazione| 1    | 30
-- Reti          | 3    | 27
-- Yoga          | NULL | NULL

-- Equivalente: LEFT JOIN invertendo l'ordine delle tabelle
-- Produce lo stesso risultato della query precedente ma usando LEFT JOIN
SELECT c.nome_corso, i.id_studente, i.voto
FROM corsi c
LEFT JOIN iscrizioni i ON c.id_corso = i.id_corso;
💡 Consiglio Pratico

L'esempio precedente ci mostra che "A RIGHT JOIN B" equivale a "B LEFT JOIN A". Per questo motivo, molti programmatori usano sempre LEFT JOIN e semplicemente invertono l'ordine delle tabelle invece di usare RIGHT JOIN. È questione di leggibilità!

FULL JOIN ⟗

📖 Definizione

Il FULL JOIN restituisce tutte le righe di ENTRAMBE le tabelle. Se non c'è corrispondenza in una delle due, le colonne mancanti sono NULL.

Studenti ⟗ Iscrizioni
⚠️ MariaDB/MySQL NON supporta FULL JOIN!

Bisogna simularlo con UNION di LEFT JOIN e RIGHT JOIN.

🎯 Obiettivo: visualizzare TUTTI gli studenti e TUTTE le iscrizioni, anche quelle senza corrispondenza

STUDENTI (Tabella A)
idnome
1Mario
2Laura
3Luca
4Anna
5Marco
ISCRIZIONI (Tabella B)
id_studenteid_corsovoto
110128
110230
210125
310327
NULL104--
Iscrizione senza studente associato
✅ RISULTATO FULL JOIN (simulato)
idnomeid_studenteid_corsovotoNote
1Mario110128✅ Match perfetto
1Mario110230✅ Match perfetto
2Laura210125✅ Match perfetto
3Luca310327✅ Match perfetto
4AnnaNULLNULLNULL⚠️ Studentessa senza iscrizioni
5MarcoNULLNULLNULL⚠️ Studente senza iscrizioni
NULLNULLNULL104--⚠️ Iscrizione senza studente
👀 Analisi

Anna e Marco appaiono con NULL nelle colonne delle iscrizioni (studenti senza iscrizioni)
Iscrizione 104 appare con NULL nelle colonne degli studenti (iscrizione senza studente associato)
Tutte le altre iscrizioni sono abbinate agli studenti corrispondenti
Nessun dato viene perso da nessuna delle due tabelle!

📜 MariaDB SQL - Simulazione FULL JOIN con righe senza corrispondenza
-- Inseriamo dati per creare uno scenario con "orfani" in entrambe le tabelle
-- Studente senza iscrizioni (Marco) e iscrizione senza studente (corso 104)
INSERT INTO studenti (id, nome, citta, eta) VALUES
    (5, 'Marco', 'Torino', 24);  -- Studente senza iscrizioni

INSERT INTO iscrizioni (id_studente, id_corso, voto) VALUES
    (NULL, 104, NULL);  -- Iscrizione senza studente associato

-- FULL JOIN simulato in MariaDB: UNION di LEFT e RIGHT JOIN
-- L'obiettivo è ottenere TUTTI i record da entrambe le tabelle,
-- incluse le righe che non hanno corrispondenza nell'altra tabella
SELECT s.id, s.nome, i.id_studente, i.id_corso, i.voto
FROM studenti s
LEFT JOIN iscrizioni i ON s.id = i.id_studente

UNION

SELECT s.id, s.nome, i.id_studente, i.id_corso, i.voto
FROM studenti s
RIGHT JOIN iscrizioni i ON s.id = i.id_studente;

-- NOTA: In altri DBMS (PostgreSQL, SQL Server) si usa direttamente FULL OUTER JOIN
-- SELECT * FROM studenti s FULL OUTER JOIN iscrizioni i ON s.id = i.id_studente;

📊 Riepilogo dei JOIN

JOIN

Solo righe con match in entrambe

LEFT JOIN

Tutto A + match da B (o NULL)

RIGHT JOIN

Match da A (o NULL) + tutto B

FULL JOIN

Tutto A + match + tutto B

ALTERNATIVE A JOIN ... ON ... : NATURAL JOIN; JOIN ... USING...

📖 Definizione

Il NATURAL JOIN è un'alternativa a "JOIN...ON..." ed èun join automatico (ovvero che non ha bisogno della specificazione ON t1.chiaveprimaria=t2.chiaveesterna): unisce le tabelle basandosi sulle colonne con lo stesso nome. Elimina automaticamente le colonne duplicate.

Tabella1 ⋈ Tabella2

🎯 Esempio Corretto: Quando le colonne corrispondono semanticamente...

STUDENTI
idnome
1Mario
2Laura
VOTI (con colonna "id")
idvoto
128
130
📜 MariaDB SQL
-- NATURAL JOIN: unisce automaticamente le tabelle sulle colonne con lo stesso nome
-- L'obiettivo è collegare studenti e voti senza specificare ON (trova 'id' in comune)
SELECT *
FROM studenti
NATURAL JOIN voti;  -- Equivale a: JOIN voti ON studenti.id = voti.id
✅ Risultato Atteso (Corretto)
idnomevoto
1Mario28
1Mario30
⚠️ I Pericoli del NATURAL JOIN - Esempi Pratici

Il NATURAL JOIN può sembrare comodo, ma nasconde insidie gravi che possono corrompere silenziosamente i tuoi risultati.

❌ Problema 1: Colonne Omonime per Coincidenza

Immagina di avere due tabelle dove "nome" appare in entrambe ma con significati diversi:

DIPENDENTI
id_dip nome stipendio
1 Mario 30000
2 Laura 35000
3 Giuseppe 28000
PROGETTI
id_dip nome budget
1 Alpha 50000
2 Beta 75000
1 Gamma 30000

🎯 Obiettivo: Trovare i progetti assegnati a ogni dipendente

❌ Query ERRATA con NATURAL JOIN
SELECT *
FROM dipendenti
NATURAL JOIN progetti;

 La query equivale a: SELECT * 
 FROM dipendenti JOIN progetti 
 ON dipendenti.id_dip = progetti.id_dip
 AND dipendenti.nome = progetti.nome  ← PROBLEMA
❌ Risultato NATURAL JOIN (SBAGLIATO!)
id_dipnomestipendiobudget
(Nessun risultato - VUOTO!)

⚠️ Nessun dipendente si chiama "Alpha", "Beta" o "Gamma"!

✅ Query CORRETTA con JOIN esplicito
SELECT d.id_dip, d.nome AS dipendente, 
       p.nome AS progetto, d.stipendio, p.budget
FROM dipendenti d
JOIN progetti p ON d.id_dip = p.id_dip;
✅ Risultato JOIN Esplicito (CORRETTO)
id_dipdipendenteprogettostipendiobudget
1MarioAlpha3000050000
1MarioGamma3000030000
2LauraBeta3500075000

❌ Problema 2: Modifiche Future allo Schema

Una query che funziona oggi può rompersi domani senza che tu modifichi nulla!

ORDINI (Oggi)
id_clientedata_ordinetotale
12024-01-15150.00
22024-01-16200.00
CLIENTI (Oggi)
id_clientenomeemail
1Mario[email protected]
2Laura[email protected]
Query con NATURAL JOIN (funziona... per ora)
SELECT *
FROM ordini
NATURAL JOIN clienti;
-- Oggi funziona: unisce solo su id_cliente ✓
✅ Risultato query prima modifica schema(funziona... per ora)
id_clientedata_ordinetotalenomeemail
12024-01-15150.00Mariomario@email.it
22024-01-16200.00Lauralaura@email.it

6 mesi dopo... Un altro sviluppatore aggiunge un campo "note" a entrambe le tabelle per scopi diversi.

ORDINI (Dopo modifica)
id_cliente data_ordine totale note
1 2024-01-15 150.00 Urgente
2 2024-01-16 200.00 Standard
CLIENTI (Dopo modifica)
id_cliente nome email note
1 Mario [email protected] VIP
2 Laura [email protected] Nuovo
❌ Stessa Query... ora ROTTA!
SELECT *
FROM ordini
NATURAL JOIN clienti;

-- ORA equivale a: SELECT *
FROM ordini JOIN clienti 
ON ordini.id_cliente = clienti.id_cliente
AND ordini.note = clienti.note   ← DISASTRO! 



-- "Urgente" ≠ "VIP", "Standard" ≠ "Nuovo"
-- Risultato: ZERO righe! La query si rompe silenziosamente!
❌ Risultato (dopo la modifica allo schema)
id_clientedata_ordinetotalenotenomeemail
(Nessun risultato - Query silenziosamente corrotta!)

❌ Problema 3: Perdita Silenziosa di Dati

A volte il NATURAL JOIN non fallisce completamente, ma perde alcune righe senza avvisarti:

PRODOTTI
id_cat nome_prodotto stato
1 Laptop attivo
1 Mouse sospeso
2 Sedia attivo
CATEGORIE
id_cat nome_categoria stato
1 Elettronica attivo
2 Arredamento attivo
❌ NATURAL JOIN perde dati
SELECT *
FROM prodotti
NATURAL JOIN categorie;

-- Unisce su: id_cat AND stato
-- Il Mouse ha stato='sospeso', la categoria ha stato='attivo'
-- → Mouse ESCLUSO dal risultato!
❌ Risultato NATURAL JOIN (manca il Mouse!)
id_catstatonome_prodottonome_categoria
1attivoLaptopElettronica
2attivoSediaArredamento
✅ JOIN Esplicito (tutti i prodotti)
id_catnome_prodottostato_prodnome_categoriastato_cat
1LaptopattivoElettronicaattivo
1 Mouse sospeso Elettronica attivo
2SediaattivoArredamentoattivo
🚫 Riepilogo: Perché Evitare NATURAL JOIN
Problema Conseguenza
Colonne omonime casuali Join su campi non correlati → risultati vuoti o errati
Schema che evolve Query che funzionano oggi si rompono domani senza preavviso
Perdita dati silenziosa Righe escluse senza errori, impossibile debuggare
Mancanza di chiarezza Impossibile capire su quali colonne avviene il join leggendo la query
✅ Best Practice: Usa Sempre JOIN Espliciti
-- ❌ MAI fare questo in produzione:
SELECT * FROM tabella1 NATURAL JOIN tabella2;

-- ✅ SEMPRE specificare la condizione:
SELECT t1.*, t2.colonna
FROM tabella1 t1
JOIN tabella2 t2 ON t1.id = t2.id_riferimento;

📌 Il JOIN esplicito è: più verboso, ma chiaro, prevedibile, manutenibile e sicuro.

La Clausola USING

📖 Definizione

La clausola USING è una sintassi alternativa a ON per specificare le condizioni di join quando le colonne di collegamento hanno lo stesso nome in entrambe le tabelle. È particolarmente utile quando si lavora con chiavi esterne che mantengono lo stesso nome della chiave primaria di riferimento. Quindi "JOIN ... USING... è un'alternativa a "JOIN... ON..."; un'ulteriore alternativa oltre a "NATURAL JOIN"

📋 Sintassi

La clausola USING richiede che le colonne utilizzate abbiano lo stesso identico nome in entrambe le tabelle coinvolte nel join.

🎯 Obiettivo

Unire le tabelle STUDENTI e ESAMI dove studenti.id = esami.id

⚠️ Requisito Fondamentale

La colonna deve chiamarsi id in entrambe le tabelle per poter usare USING.

📌 Esempio Pratico: ON vs USING

🎯 Obiettivo: Collegare studenti ai loro esami utilizzando entrambi i metodi

STUDENTI
idnome
1Mario
2Laura
3Luca
ESAMI
idid_corsovoto
110128
110230
210125

🔴 Metodo 1: JOIN con ON

📜 SQL - Usando ON
-- JOIN con ON: bisogna specificare tabella.colonna per entrambi i lati
-- L'obiettivo è collegare studenti ed esami tramite l'id comune
SELECT *
FROM studenti
JOIN esami ON studenti.id = esami.id;
✅ RISULTATO (con ON)
studenti.id nome esami.id id_corso voto
1Mario110128
1Mario110230
2Laura210125
👀 Nota Importante

Con ON, vengono mostrate entrambe le colonne id (quella in STUDENTI e quella in ESAMI), anche se contengono gli stessi valori e si riferiscono allo stesso soggetto (id ello studente). Questo può creare confusione ma basta nel SELECT specificare le colonne per ovviare a ciò

🟢 Metodo 2: JOIN con USING

📜 SQL - Usando USING
-- USING è più pulito: non serve specificare le colonne
SELECT *
FROM studenti
JOIN esami USING (id);
✅ RISULTATO (con USING)
id nome id_corso voto
1Mario10128
1Mario10230
2Laura10125
💡 Vantaggio di USING

Con USING, la colonna id appare una sola volta nel risultato! Il database sa che è la stessa colonna in entrambe le tabelle e la mostra una sola volta. Molto più pulito!

⚖️ Confronto Diretto ON vs USING

🔴 ON (due colonne duplicate)

studenti.idnomeesami.idvoto
1Mario128
1Mario130

❌ Due colonne ID identiche → confusione

🟢 USING (una colonna sola)

idnomevoto
1Mario28
1Mario30

✅ Una sola colonna ID → più chiaro

📌 USING con LEFT JOIN e RIGHT JOIN

🎯 Obiettivo: USING funziona con tutti i tipi di JOIN!

📜 SQL - USING con LEFT JOIN
-- LEFT JOIN con USING: tutti gli studenti, anche senza esami
SELECT *
FROM studenti
LEFT JOIN esami USING (id);

-- RIGHT JOIN con USING: tutti gli esami, anche senza studenti
SELECT *
FROM studenti
RIGHT JOIN esami USING (id);
💡 Suggerimento

Quando usi USING con LEFT JOIN o RIGHT JOIN, puoi facilmente identificare le righe senza corrispondenza perché le colonne della tabella "perduta" saranno tutte NULL.

⚠️ Quando le colonne hanno NOMI DIVERSI

Se le colonne hanno nomi diversi, USING non funziona! Devi usare ON.

STUDENTI
idnome
1Mario
ISCRIZIONI
id_studenteid_corso
1101
📜 SQL - Esempio con nomi diversi
-- ❌ Questo dà ERRORE! La colonna 'id' non esiste in ISCRIZIONI
-- SELECT * FROM studenti JOIN iscrizioni USING (id);-> errore


-- ✅ USA ON con nomi diversi SELECT * FROM studenti JOIN iscrizioni ON studenti.id = iscrizioni.id_studente;

📌 USING con PIÙ COLONNE

🎯 Obiettivo: USING può unire su più colonne contemporaneamente!

📜 SQL - USING con più colonne
-- Join su due colonne che devono avere lo stesso nome
SELECT *
FROM tabella1
JOIN tabella2 USING (colonna1, colonna2);

-- Equivalente a:
-- SELECT * FROM tabella1 
-- JOIN tabella2 ON tabella1.colonna1 = tabella2.colonna1 
-- AND tabella1.colonna2 = tabella2.colonna2;
📋 Riepilogo: Quando Usare USING
Scenario Clausola Consigliata
Colonne con lo stesso nome ✅ USING (più pulito)
Colonne con nomi diversi ❌ ON (obbligatorio)
Condizioni di join complesse (AND/OR) ❌ ON (necessario)
Join su più colonne con stesso nome ✅ USING (col1, col2)
Vuoi evitare colonne duplicate nel risultato ✅ USING (più elegante)
💡 Best Practice

Se le colonne hanno lo stesso nome e vuoi un risultato più pulito, usa USING. Se i nomi sono diversi o hai bisogno di condizioni complesse, usa ON. Entrambi sono validi e standard SQL, la scelta dipende dalla situazione!

🔍 ON vs USING: Qual è la Differenza?

📊 Tabella di Sintesi
Aspetto ON USING
Flessibilità nomi colonna Colonne possono avere nomi diversi Deve avere lo stesso nome
Clausola ON tabella1.colonna1 = tabella2.colonna2 USING (colonna_comune)
Leggibilità Più verboso, ma esplicito Più conciso e pulito
Output Mantiene entrambe le colonne Mostra una sola colonna
Uso tipico Join con nomi diversi o condizioni complesse Join su chiavi con nomi identici

🎯 Operazioni Insiemistiche

📋 Requisito Fondamentale

Per UNION, INTERSECT e EXCEPT le tabelle devono essere union-compatibili ovvero avere stesso numero di colonne e tipi compatibili!

Per gli esempi qui sotto, assumiamo di avere due tabelle studenti_roma e studenti_db.

STUDENTI_ROMA
idnome
1Mario
3Luca
STUDENTI_DB
idnome
1Mario
2Laura

UNIONE (∪ - UNION)

📖 Definizione

L'unione combina i risultati di due query, restituendo tutte le righe di entrambe senza duplicati (per default).

A ∪ B
STUDENTI_ROMA
idnome
1Mario
3Luca
STUDENTI_DB
idnome
1Mario
2Laura
=
RISULTATO
idnome
1Mario
2Laura
3Luca
📜 MariaDB SQL
-- UNION: combina le righe di due tabelle, eliminando i duplicati
-- L'obiettivo è avere un elenco unico di tutti gli studenti (Roma + DB)
SELECT * FROM studenti_roma
UNION
SELECT * FROM studenti_db;

-- UNION ALL: combina le righe di due tabelle, MANTENENDO i duplicati
-- L'obiettivo è avere TUTTE le righe, anche se si ripetono (Mario appare 2 volte)
SELECT * FROM studenti_roma
UNION ALL
SELECT * FROM studenti_db;

INTERSEZIONE (∩ - INTERSECT)

📖 Definizione

L'intersezione restituisce solo le righe che appaiono in entrambe le query.

A ∩ B
STUDENTI_ROMA
idnome
1Mario
3Luca
STUDENTI_DB
idnome
1Mario
2Laura
=
RISULTATO
idnome
1Mario
⚠️ MariaDB NON supporta INTERSECT direttamente!

Bisogna simularlo con JOIN o subquery.

📜 MariaDB SQL - Simulazione INTERSECT
-- INTERSECT simulato con IN: trova studenti presenti in ENTRAMBE le tabelle
-- L'obiettivo è trovare chi è sia a Roma che iscritto al DB (solo Mario)
SELECT * 
FROM studenti_roma
WHERE id IN (
    SELECT id FROM studenti_db
);

-- INTERSECT simulato con JOIN: alternativa più efficiente
-- L'obiettivo è lo stesso: trovare studenti comuni alle due tabelle
SELECT a.*
FROM studenti_roma a
JOIN studenti_db b ON a.id = b.id;

-- NOTA: In altri DBMS (PostgreSQL, SQL Server) si usa direttamente INTERSECT
-- SELECT * FROM studenti_roma INTERSECT SELECT * FROM studenti_db;

DIFFERENZA (− - EXCEPT)

📖 Definizione

La differenza restituisce le righe che sono nella prima tabella ma NON nella seconda.

A − B
STUDENTI_ROMA
idnome
1Mario
3Luca
STUDENTI_DB
idnome
1Mario
2Laura
=
RISULTATO
idnome
3Luca
⚠️ MariaDB NON supporta EXCEPT direttamente!

Bisogna simularlo con LEFT JOIN + WHERE NULL o NOT IN.

📜 MariaDB SQL - Simulazione EXCEPT
-- EXCEPT simulato con NOT IN: trova studenti che sono presenti SOLO nella prima tabella
-- L'obiettivo è trovare chi è a Roma ma NON è iscritto al DB (solo Luca)
SELECT * 
FROM studenti_roma
WHERE id NOT IN (
    SELECT id FROM studenti_db
);

-- EXCEPT simulato con LEFT JOIN + IS NULL: alternativa più efficiente
-- L'obiettivo è lo stesso: trovare studenti presenti solo nella prima tabella
SELECT a.*
FROM studenti_roma a
LEFT JOIN studenti_db b ON a.id = b.id
WHERE b.id IS NULL;

-- NOTA: In altri DBMS (PostgreSQL, SQL Server) si usa direttamente EXCEPT
-- SELECT * FROM studenti_roma EXCEPT SELECT * FROM studenti_db;