Benvenuto in una nuova lezione su SQL! Oggi ci conosciamo con gli operatori più invisibili ma incredibilmente potenti — EXISTS e NOT EXISTS. Immagina uno 007 che non lascia tracce, ma ti dice subito: "Sì, l'oggetto esiste" oppure "No, qui è vuoto". Questi operatori non restituiscono dati direttamente, ma ti permettono di fare controlli logici precisi nelle query.
Partiamo dalle basi. EXISTS — è un operatore che controlla l'esistenza delle righe nel risultato di una subquery. Se la subquery restituisce almeno una riga, la condizione EXISTS restituisce TRUE, altrimenti — FALSE.
SELECT 1
WHERE EXISTS (
SELECT *
FROM students
WHERE grade > 3.5
);
Come vedi, non ci interessano i dati stessi della subquery, ma solo il fatto che esistano tali righe. Se almeno una riga soddisfa la condizione, la query restituisce 1.
Sintassi di EXISTS
La sintassi di EXISTS è semplice:
SELECT colonne
FROM tabella
WHERE EXISTS (
SELECT 1
FROM altra_tabella
WHERE condizione
);
Spiegazione:
- La subquery dentro
EXISTSpuò essere qualsiasi query. - È proprio il risultato della subquery che decide se torna
TRUEoFALSE.
Esempio: Ci sono studenti con voto sopra 4?
Immaginiamo la tabella students:
| id | name | grade |
|---|---|---|
| 1 | Otto | 3.2 |
| 2 | Anna | 4.7 |
| 3 | Dan | 5.0 |
| 4 | Lina | 2.9 |
Supponiamo di voler controllare se esistono studenti con voto sopra 4. Usiamo questa query:
SELECT 'Ci sono studenti con voto alto!'
WHERE EXISTS (
SELECT 1
FROM students
WHERE grade > 4
);
Risultato:
Ci sono studenti con voto alto!
Perché EXISTS è più veloce di IN?
Il vantaggio principale di EXISTS è che ferma l'esecuzione della subquery appena trova la prima corrispondenza. Questo significa che se hai una condizione che controlla l'esistenza dei dati, EXISTS può essere super efficiente.
Per esempio, immagina che nella tabella students ci siano milioni di righe, ma cerchiamo solo una corrispondenza (grade > 4). Appena SQL trova la prima riga che va bene, la query si ferma.
Uso di NOT EXISTS
Ora parliamo di NOT EXISTS. Questo operatore funziona come l'opposto di EXISTS. Restituisce TRUE se la subquery non restituisce nessuna riga.
Esempio: trovare studenti senza voti (NULL)
Supponiamo che nella nostra tabella ci siano studenti che ancora non hanno un voto:
| id | name | grade |
|---|---|---|
| 1 | Otto | NULL |
| 2 | Anna | 4.7 |
| 3 | Dan | 5.0 |
| 4 | Lina | NULL |
Vogliamo selezionare tutti gli studenti senza voto. Usiamo NOT EXISTS:
SELECT *
FROM students s
WHERE NOT EXISTS (
SELECT 1
FROM students
WHERE grade IS NOT NULL
AND id = s.id
);
Risultato:
| id | name | grade |
|---|---|---|
| 1 | Otto | NULL |
| 4 | Lina | NULL |
Confronto tra EXISTS e IN
A volte sembra che EXISTS e IN facciano la stessa cosa. A prima vista sì, ma ci sono delle sfumature. Soprattutto se da qualche parte c'è un NULL. Allora il comportamento di IN può diventare strano, mentre EXISTS ti salva.
Dai, vediamo con un esempio.
Tabella courses (corsi che puoi seguire):
| course_id | name |
|---|---|
| 1 | Matematica |
| 2 | Storia |
Ecco gli studenti:
| student_id | name |
|---|---|
| 1 | Alex Lin |
| 2 | Anna Song |
| 3 | Maria Chi |
| 4 | Dan Seth |
| 5 | Shadow Moon |
Tabella enrollments (chi si è iscritto a quale corso):
| student_id | course_id |
|---|---|
| 1 | 1 |
| 2 | 2 |
| 3 | NULL |
Vogliamo selezionare i nomi dei corsi dove qualcuno si è iscritto. Sembra facile.
Usando IN:
SELECT name
FROM courses
WHERE course_id IN (
SELECT course_id
FROM enrollments
);
A prima vista dovrebbe funzionare. Ma se in enrollments c'è NULL in courseid, come per Maria Chi, IN può restituire... niente! Perché NULL rende la subquery "indefinita", e SQL si confonde: magari NULL è proprio quel courseid che cerchiamo?
Usando EXISTS:
SELECT name
FROM courses c
WHERE EXISTS (
SELECT 1
FROM enrollments e
WHERE c.course_id = e.course_id
);
Invece EXISTS controlla: "C'è almeno una riga dove course_id coincide?" — e basta. Non si preoccupa se c'è un NULL da qualche parte, perché cerca solo corrispondenze concrete, non una lista di valori.
Morale: se nella subquery può esserci NULL, meglio usare EXISTS per non avere sorprese.
Esempi di casi reali
Tabella students:
| id | name |
|---|---|
| 1 | Alex Lin |
| 2 | Anna Song |
| 3 | Maria Chi |
| 4 | Dan Seth |
| 5 | Shadow Moon |
Tabella enrollments:
| student_id | course_id |
|---|---|
| 1 | 1 |
| 2 | 2 |
| 3 | NULL |
Esempio 1. Studenti iscritti ai corsi
Troviamo chi si è già iscritto a qualche corso (anche in modo strano, come Maria Chi):
SELECT name
FROM students s
WHERE EXISTS (
SELECT 1
FROM enrollments e
WHERE s.id = e.student_id
);
Risultato:
Alex Lin
Anna Song
Maria Chi
Se uno studente appare in enrollments — viene selezionato, anche se il suo course_id non è chiaro.
Esempio 2. Studenti senza corsi
Ora troviamo chi esiste nel sistema — ma non si è ancora iscritto a nulla:
SELECT name
FROM students s
WHERE NOT EXISTS (
SELECT 1
FROM enrollments e
WHERE s.id = e.student_id
);
Risultato:
Dan Seth
Shadow Moon
Sembra che questi due non abbiano ancora trovato il corso giusto. O magari si sono dimenticati di iscriversi :)
Esempio 3. Selezione dei corsi con più di 5 studenti iscritti
Tabella courses:
| course_id | name |
|---|---|
| 1 | Matematica |
| 2 | Storia |
| 3 | Biologia |
| 4 | Filosofia |
Tabella enrollments:
| student_id | course_id |
|---|---|
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
| 4 | 1 |
| 5 | 1 |
| 6 | 1 |
| 7 | 2 |
| 8 | 2 |
| 9 | 2 |
| 10 | NULL |
Vogliamo trovare quei corsi dove si sono iscritti più di cinque studenti. Qui EXISTS chiede: "C'è almeno un gruppo di righe per questo corso dove gli studenti sono più di cinque?"
SELECT name
FROM courses c
WHERE EXISTS (
SELECT 1
FROM enrollments e
WHERE c.course_id = e.course_id
GROUP BY e.course_id
HAVING COUNT(*) > 5
);
Risultato:
Matematica
Solo al corso "Matematica" (course_id = 1) si sono iscritti sei studenti. Gli altri corsi per ora non sono così popolari.
Errori comuni con EXISTS e NOT EXISTS
- Non capire bene la sintassi della subquery. Controlla sempre che la subquery faccia riferimento correttamente alla tabella esterna.
- Dimenticare il controllo di
NULL. Anche se usiEXISTS, a volte devi gestireNULLin modo esplicito. - Mancanza di indice sui campi della subquery. Questo può rallentare molto l'esecuzione della query.
Per oggi è tutto! Ora sai come usare EXISTS e NOT EXISTS per controllare l'esistenza dei dati, e anche le differenze tra questi operatori e IN. Nella prossima lezione continueremo a esplorare le subquery, vedendo come usarle in SELECT per lavorare con dati aggregati.
GO TO FULL VERSION