Guida Completa con Esempi in MariaDB/MySQL
Database con Studenti, Corsi e Esame (tabella di collegamento).
| id | nome | cittΓ | etΓ |
|---|---|---|---|
| 1 | Mario | Roma | 22 |
| 2 | Laura | Milano | 20 |
| 3 | Luca | Roma | 23 |
| 4 | Anna | Napoli | 21 |
| id_corso | nome_corso | docente |
|---|---|---|
| 101 | Database | Rossi |
| 102 | Programmazione | Bianchi |
| 103 | Reti | Verdi |
| 104 | Yoga | Aldi |
| id_studente | id_corso | voto |
|---|---|---|
| 1 | 101 | 28 |
| 1 | 102 | 30 |
| 2 | 101 | 25 |
| 3 | 103 | 27 |
-- Creazione del database CREATE DATABASE IF NOT EXISTS universita; USE universita; CREATE TABLE studenti ( id INT PRIMARY KEY, nome VARCHAR(50), citta VARCHAR(50), eta INT ); CREATE TABLE corsi ( id_corso INT PRIMARY KEY, nome_corso VARCHAR(50), docente VARCHAR(50) ); 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) );
La selezione filtra le righe di una tabella in base a una condizione. Restituisce solo le tuple che soddisfano la condizione. Opera sulle RIGHE (orizzontalmente). Il numero di colonne resta invariato.
π― Esempio: Studenti di Roma
| id | nome | cittΓ | etΓ |
|---|---|---|---|
| 1 | Mario | Roma | 22 |
| 2 | Laura | Milano | 20 |
| 3 | Luca | Roma | 23 |
| 4 | Anna | Napoli | 21 |
| id | nome | cittΓ | etΓ |
|---|---|---|---|
| 1 | Mario | Roma | 22 |
| 3 | Luca | Roma | 23 |
-- Selezione semplice SELECT * FROM studenti WHERE citta = 'Roma'; -- Con AND: Roma E piΓΉ di 21 anni SELECT * FROM studenti WHERE citta = 'Roma' AND eta > 21; -- Con OR / IN: Roma o Milano SELECT * FROM studenti WHERE citta IN ('Roma', 'Milano');
La proiezione seleziona solo alcune colonne dalla tabella, eliminando le altre. Opera sulle COLONNE (verticalmente). Il numero di righe puΓ² diminuire con DISTINCT.
π― Esempio: Solo nome e cittΓ degli studenti
| nome | cittΓ |
|---|---|
| Mario | Roma |
| Laura | Milano |
| Luca | Roma |
| Anna | Napoli |
-- Proiezione semplice SELECT nome, citta FROM studenti; -- Eliminare duplicati (come in algebra relazionale) SELECT DISTINCT citta FROM studenti; -- Risultato: Roma, Milano, Napoli (3 righe, non 4) -- Combinazione Selezione + Proiezione: Ο cittΓ ='Roma' β Ο nome SELECT nome FROM studenti WHERE citta = 'Roma';
Combina ogni riga della prima tabella con ogni riga della seconda. Genera tutte le combinazioni possibili. Il computer non sa quali righe appartengono a chi: sarΓ poi compito del JOIN filtrare solo quelle dove gli ID corrispondono.
| id | nome |
|---|---|
| 1 | Mario |
| 2 | Laura |
| id_studente | voto |
|---|---|
| 1 | 28 |
| 2 | 30 |
| id | nome | id_studente | voto |
|---|---|---|---|
| 1 | Mario | 1 | 28 |
| 1 | Mario | 2 | 30 |
| 2 | Laura | 1 | 28 |
| 2 | Laura | 2 | 30 |
Il JOIN Γ¨ un Prodotto Cartesiano filtrato: elimina le righe dove gli ID non corrispondono e tiene solo quelle valide.
SELECT * FROM studenti CROSS JOIN esami;
Prodotto cartesiano filtrato da una condizione. Restituisce solo le righe dove la condizione Γ¨ soddisfatta.
JOIN e INNER JOIN sono equivalenti (la parola INNER Γ¨ opzionale).
| id | nome | id_studente | id_corso | voto |
|---|---|---|---|---|
| 1 | Mario | 1 | 101 | 28 |
| 1 | Mario | 1 | 102 | 30 |
| 2 | Laura | 2 | 101 | 25 |
| 3 | Luca | 3 | 103 | 27 |
Anna (id=4) non appare perchΓ© non ha esami. Mario appare 2 volte perchΓ© ha 2 esami.
-- INNER JOIN base SELECT * FROM studenti s INNER JOIN esami e ON s.id = e.id_studente; -- Equivalente (JOIN senza INNER = stesso risultato) SELECT * FROM studenti s JOIN esami e ON s.id = e.id_studente;
π― Nome studente, nome corso e voto (3 tabelle collegate).
SELECT s.nome, c.nome_corso, e.voto FROM studenti s JOIN esami e ON s.id = e.id_studente JOIN corsi c ON e.id_corso = c.id;
π― Abbinare dipendenti ai turni per reparto E sede.
-- Join su DUE colonne per evitare errori 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;
π― Prodotti nel budget del cliente (budget β₯ prezzo). Il Theta Join usa qualsiasi operatore (=, <, >, <=, >=, <>, LIKE, BETWEEN). L'Equi-Join Γ¨ il caso particolare con "=".
SELECT c.nome AS cliente, c.budget, p.nome AS prodotto, p.prezzo FROM clienti c JOIN prodotti p ON c.budget >= p.prezzo;
ON: condizione per collegare le tabelle (relazione) | WHERE: filtro sui risultati (selezione)
-- Esami con voto >= 28 di studenti il cui nome inizia per 'M' SELECT s.nome, e.voto FROM studenti s JOIN esami e ON s.id = e.id_studente WHERE s.nome LIKE 'M%' AND e.voto >= 28;
Restituisce tutte le righe della tabella di SINISTRA + le corrispondenze dalla destra. Se non c'Γ¨ corrispondenza, le colonne della destra sono NULL.
π― Esempio: Tutti gli studenti CON i loro esami (anche chi non ha esami)
| id | nome | id_corso | voto |
|---|---|---|---|
| 1 | Mario | 101 | 28 |
| 1 | Mario | 102 | 30 |
| 2 | Laura | 101 | 25 |
| 3 | Luca | 103 | 27 |
| 4 | Anna | NULL | NULL |
Anna appare! Anche senza esami, viene inclusa con NULL perchΓ© Γ¨ nella tabella di SINISTRA.
-- LEFT JOIN: tutti gli studenti, anche senza esami 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) SELECT s.* FROM studenti s LEFT JOIN iscrizioni i ON s.id = i.id_studente WHERE i.id_studente IS NULL;
Restituisce tutte le righe della tabella di DESTRA + le corrispondenze dalla sinistra. Se non c'Γ¨ corrispondenza, le colonne della sinistra sono NULL.
π― Esempio: Tutti i corsi con chi Γ¨ iscritto (incluso Yoga senza iscritti)
| nome_corso | id_studente | voto |
|---|---|---|
| Database | 1 | 28 |
| Database | 2 | 25 |
| Programmazione | 1 | 30 |
| Reti | 3 | 27 |
| Yoga | NULL | NULL |
-- RIGHT JOIN: tutti i corsi, anche senza iscritti SELECT c.nome_corso, i.id_studente, i.voto FROM iscrizioni i RIGHT JOIN corsi c ON i.id_corso = c.id_corso; -- Equivalente: LEFT JOIN invertendo l'ordine delle tabelle SELECT c.nome_corso, i.id_studente, i.voto FROM corsi c LEFT JOIN iscrizioni i ON c.id_corso = i.id_corso;
Scrivere iscrizioni RIGHT JOIN corsi Γ¨ identico a scrivere corsi LEFT JOIN iscrizioni.
In entrambi i casi il risultato contiene tutti i corsi (incluso Yoga) + le iscrizioni abbinate.
Per questo motivo, molti programmatori preferiscono usare sempre LEFT JOIN
e, quando servirebbe RIGHT JOIN, invertono semplicemente l'ordine delle tabelle:
invece di A RIGHT JOIN B scrivono B LEFT JOIN A β il risultato Γ¨ identico ma la query Γ¨ piΓΉ leggibile.
Restituisce tutte le righe di ENTRAMBE le tabelle. Se non c'Γ¨ corrispondenza, le colonne mancanti sono NULL. Nessun dato viene perso!
Si simula con UNION di LEFT JOIN e RIGHT JOIN.
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; -- In altri DBMS: SELECT * FROM studenti s FULL OUTER JOIN iscrizioni i ON s.id = i.id_studente;
Solo righe con match in entrambe
Tutto A + match da B (o NULL)
Match da A (o NULL) + tutto B
Tutto A + match + tutto B
Join automatico (non serve ON): unisce le tabelle basandosi sulle colonne con lo stesso nome. Elimina automaticamente le colonne duplicate.
-- NATURAL JOIN: unisce automaticamente sulle colonne con lo stesso nome SELECT * FROM studenti NATURAL JOIN voti; -- Equivale a: JOIN voti ON studenti.id = voti.id
| Problema | Conseguenza |
|---|---|
| Colonne omonime per coincidenza | Join su campi non correlati β risultati vuoti o errati |
| Schema che evolve | Query funzionanti 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 |
Usa sempre JOIN esplicito con ON: Γ¨ piΓΉ verboso ma chiaro, prevedibile, manutenibile e sicuro.
Alternativa a ON per specificare le condizioni di join quando le colonne di collegamento hanno lo stesso nome in entrambe le tabelle. La colonna appare una sola volta nel risultato (piΓΉ pulito).
Nel nostro setup, la tabella ESAME ha la colonna id_studente (chiave esterna), mentre STUDENTI ha id (chiave primaria).
Avendo nomi diversi, USING non puΓ² essere usato β bisogna usare ON:
-- STUDENTI ha la colonna "id", ESAME ha la colonna "id_studente" -- I nomi sono diversi β non si puΓ² usare USING, serve ON SELECT * FROM studenti s JOIN esame e ON s.id = e.id_studente;
Se entrambe le tabelle hanno una colonna chiamata id che si riferisce alla stessa entitΓ (es. l'id dello studente), allora USING Γ¨ piΓΉ pulito:
| id | nome |
|---|---|
| 1 | Mario |
| 2 | Laura |
id = chiave primaria dello studente
| id | voto |
|---|---|
| 1 | 28 |
| 1 | 30 |
id = chiave esterna β studente (stesso significato!)
| studenti.id | nome | voti.id | voto |
|---|---|---|---|
| 1 | Mario | 1 | 28 |
β La colonna id appare due volte (valori identici β ridondanza)
| id | nome | voto |
|---|---|---|
| 1 | Mario | 28 |
β La colonna id appare una sola volta β piΓΉ pulito
-- Metodo 1: JOIN con ON (funziona sempre, anche con nomi diversi) SELECT * FROM studenti JOIN voti ON studenti.id = voti.id; -- Metodo 2: JOIN con USING (solo se la colonna ha lo STESSO nome in entrambe le tabelle) SELECT * FROM studenti JOIN voti USING (id); -- USING con LEFT JOIN SELECT * FROM studenti LEFT JOIN voti USING (id); -- USING con PIΓ colonne (se entrambe le tabelle hanno col1 e col2) SELECT * FROM tabella1 JOIN tabella2 USING (colonna1, colonna2);
| Scenario | Clausola |
|---|---|
Colonne con nomi diversi (es. id vs id_studente) | β ON (obbligatorio β USING darebbe errore) |
| Colonne con stesso nome e stesso significato | β USING (piΓΉ pulito) |
| Condizioni complesse (AND/OR) | β ON (necessario) |
| Evitare colonne duplicate nel risultato | β USING (colonna mostrata una sola volta) |
Per UNION, INTERSECT e EXCEPT le tabelle devono essere union-compatibili: stesso numero di colonne e tipi compatibili.
| id | nome |
|---|---|
| 1 | Mario |
| 3 | Luca |
| id | nome |
|---|---|
| 1 | Mario |
| 2 | Laura |
Combina i risultati di due query, restituendo tutte le righe di entrambe senza duplicati (per default).
| id | nome |
|---|---|
| 1 | Mario |
| 3 | Luca |
| id | nome |
|---|---|
| 1 | Mario |
| 2 | Laura |
| id | nome |
|---|---|
| 1 | Mario |
| 2 | Laura |
| 3 | Luca |
-- UNION: senza duplicati SELECT * FROM studenti_roma UNION SELECT * FROM studenti_db; -- UNION ALL: mantenendo i duplicati (Mario appare 2 volte) SELECT * FROM studenti_roma UNION ALL SELECT * FROM studenti_db;
Restituisce solo le righe che appaiono in entrambe le query.
| id | nome |
|---|---|
| 1 | Mario |
| 3 | Luca |
| id | nome |
|---|---|
| 1 | Mario |
| 2 | Laura |
| id | nome |
|---|---|
| 1 | Mario |
Si simula con JOIN o subquery.
-- Con IN: trova studenti presenti in ENTRAMBE le tabelle SELECT * FROM studenti_roma WHERE id IN (SELECT id FROM studenti_db); -- Con JOIN: alternativa piΓΉ efficiente SELECT a.* FROM studenti_roma a JOIN studenti_db b ON a.id = b.id;
Restituisce le righe che sono nella prima tabella ma NON nella seconda.
| id | nome |
|---|---|
| 1 | Mario |
| 3 | Luca |
| id | nome |
|---|---|
| 1 | Mario |
| 2 | Laura |
| id | nome |
|---|---|
| 3 | Luca |
Si simula con LEFT JOIN + WHERE NULL o NOT IN.
-- Con NOT IN: studenti presenti SOLO nella prima tabella SELECT * FROM studenti_roma WHERE id NOT IN (SELECT id FROM studenti_db); -- Con LEFT JOIN + IS NULL: alternativa piΓΉ efficiente SELECT a.* FROM studenti_roma a LEFT JOIN studenti_db b ON a.id = b.id WHERE b.id IS NULL;
Filtra righe
WHERE condizione
Seleziona colonne
SELECT col1, col2
Tutte le combinazioni possibili
CROSS JOIN
Solo righe con match in entrambe
JOIN ... ON
Join automatico (β οΈ pericoloso)
NATURAL JOIN
Join su colonne con stesso nome
JOIN ... USING(col)
Tutto A + match B (o NULL)
LEFT JOIN ... ON
Match A (o NULL) + tutto B
RIGHT JOIN ... ON
Tutto A + tutto B (simulato)
LEFT UNION RIGHT
Combina righe di A e B
UNION / UNION ALL
Righe in comune (simulato)
IN / JOIN
Righe di A non in B (simulato)
NOT IN / LEFT JOIN + IS NULL
-- βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ -- OPERATORI RELAZIONALI - MARIADB -- βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ -- SELEZIONE (Ο) - filtra righe SELECT * FROM tabella WHERE condizione; -- PROIEZIONE (Ο) - seleziona colonne SELECT col1, col2 FROM tabella; SELECT DISTINCT col1 FROM tabella; -- senza duplicati -- PRODOTTO CARTESIANO (Γ) SELECT * FROM A CROSS JOIN B; SELECT * FROM A, B; -- equivalente -- INNER JOIN (β) - solo match SELECT * FROM A JOIN B ON A.id = B.id; -- JOIN con USING - quando le colonne hanno lo stesso nome SELECT * FROM A JOIN B USING (id); -- NATURAL JOIN - join automatico (sconsigliato) SELECT * FROM A NATURAL JOIN B; -- LEFT JOIN (β) - tutto A + match B SELECT * FROM A LEFT JOIN B ON A.id = B.id; -- RIGHT JOIN (β) - match A + tutto B (equivale a B LEFT JOIN A) SELECT * FROM A RIGHT JOIN B ON A.id = B.id; -- FULL JOIN (β) - simulato in MariaDB SELECT * FROM A LEFT JOIN B ON A.id = B.id UNION SELECT * FROM A RIGHT JOIN B ON A.id = B.id; -- UNIONE (βͺ) SELECT * FROM A UNION SELECT * FROM B; -- senza duplicati SELECT * FROM A UNION ALL SELECT * FROM B; -- con duplicati -- INTERSEZIONE (β©) - simulata in MariaDB SELECT * FROM A WHERE id IN (SELECT id FROM B); -- DIFFERENZA (β) - simulata in MariaDB SELECT * FROM A WHERE id NOT IN (SELECT id FROM B); SELECT A.* FROM A LEFT JOIN B ON A.id = B.id WHERE B.id IS NULL;