Ormai sai già come unire le tabelle usando JOIN. Ma nella vita reale la semplice corrispondenza delle chiavi spesso non basta. Spesso ti serve unire i dati solo se rispettano un criterio aggiuntivo — tipo solo i record attivi, solo i dati dell’anno corrente o solo gli ordini completati.
Ed è qui che entra in gioco l’estensione della clausola ON con AND.
Le condizioni aggiuntive in JOIN ... ON ti permettono di controllare esattamente quali righe partecipano al join, ancora prima che SQL inizi a costruire il risultato. Questo rende la query:
- Più veloce (meno righe passano attraverso il
JOIN), - Più precisa (il filtraggio avviene già nella fase di join),
- Più prevedibile quando usi
LEFT JOIN(diverso dal filtraggio inWHERE).
Esempio: Solo iscrizioni attive ai corsi
Supponiamo che la tabella enrollments abbia lo stato di partecipazione dello studente: active, dropped, pending.
Tabella students:
| id | name |
|---|---|
| 1 | Otto Song |
| 2 | Maria Chi |
| 3 | Alex Lin |
Aggiorniamo la tabella enrollments:
| student_id | course_id | status |
|---|---|---|
| 1 | 101 | active |
| 1 | 103 | active |
| 2 | 102 | dropped |
| 3 | 101 | active |
Tabella courses:
| id | name |
|---|---|
| 101 | Mathematics |
| 102 | Physics |
| 103 | Computer Science |
Ora vogliamo ottenere solo gli studenti che hanno corsi attivi:
SELECT
students.name AS student_name,
courses.name AS course_name
FROM students
INNER JOIN enrollments
ON students.id = enrollments.student_id
AND enrollments.status = 'active'
INNER JOIN courses
ON enrollments.course_id = courses.id;
Risultato:
| student_name | course_name |
|---|---|
| Otto Song | Mathematics |
| Otto Song | Computer Science |
| Alex Lin | Mathematics |
Qui abbiamo aggiunto AND enrollments.status = 'active' dentro ON, così il join avviene solo sui record attivi, invece di filtrare dopo l’unione.
Perché non WHERE?
Potevi anche scrivere così:
...
WHERE enrollments.status = 'active'
Ma questo si comporta diversamente con LEFT JOIN. Il filtraggio in WHERE elimina le righe senza corrispondenza (NULL), e così trasforma il LEFT JOIN in un INNER JOIN.
Invece la condizione AND enrollments.status = 'active' dentro ON limita subito le righe unite — decide quali righe entrano proprio nel join, non solo filtra il risultato dopo.
Questo approccio è super importante se vuoi mantenere le righe di una tabella anche se nell’altra non ci sono valori corrispondenti (cosa che succede spesso nei report e nelle analisi).
Altri esempi di utilizzo di ON ... AND ...
Tabella students:
| id | name |
|---|---|
| 1 | Otto Song |
| 2 | Maria Chi |
| 3 | Alex Lin |
Tabella enrollments:
| student_id | course_id | status | enrolled_at |
|---|---|---|---|
| 1 | 101 | active | 2025-02-01 |
| 1 | 103 | active | 2025-03-05 |
| 2 | 102 | dropped | 2024-05-15 |
| 3 | 101 | active | 2025-03-12 |
Tabella courses:
| id | name |
|---|---|
| 101 | Math |
| 102 | Physics |
| 103 | CS |
Esempio: solo corsi dell’anno corrente
SELECT
students.name,
courses.name,
enrollments.enrolled_at
FROM students
JOIN enrollments
ON students.id = enrollments.student_id
AND EXTRACT(YEAR FROM enrollments.enrolled_at) = EXTRACT(YEAR FROM CURRENT_DATE)
JOIN courses
ON enrollments.course_id = courses.id;
Qui uniamo solo i record che riguardano l’anno corrente.
| name | name | enrolled_at |
|---|---|---|
| Otto Song | Math | 2025-02-01 |
| Otto Song | CS | 2025-03-05 |
| Alex Lin | Math | 2025-03-12 |
Esempio: esclusione per valore
JOIN enrollments
ON students.id = enrollments.student_id
AND enrollments.status != 'dropped'
Escludiamo gli studenti che si sono ritirati già nella fase di join, non dopo il filtraggio.
| name | name |
|---|---|
| Otto Song | Math |
| Otto Song | CS |
| Alex Lin | Math |
Quando la condizione è dentro ON, PostgreSQL può ottimizzare il piano di join e lavorare su meno righe. Questo è super importante con tanti dati. Il filtraggio interno è più efficiente che "scartare" dopo il JOIN.
JOIN ON — non solo chiavi
Tanti pensano che ON sia solo id = id. In realtà puoi metterci dentro:
- Operatori logici:
AND,OR,NOT - Confronti:
>,<,<>,BETWEEN,IN - Espressioni:
EXTRACT,DATE_TRUNC,COALESCE,NULLIF
Mettiamo tutto insieme
Tabella students:
| id | name |
|---|---|
| 1 | Otto Song |
| 2 | Maria Chi |
| 3 | Alex Lin |
Tabella faculties:
| id | name | |
|---|---|---|
| 10 | Engineering | |
| 20 | Natural Sciences | |
| 30 | ← senza nome (NULL) |
Tabella courses:
| id | name | teacher | faculty_id |
|---|---|---|---|
| 101 | Math | Liam Park | 10 |
| 102 | Physics | Chloe Zhang | 20 |
| 103 | CS | Noah Kim | 10 |
| 104 | PE | Ava Chen | 30 |
Tabella enrollments:
| student_id | course_id | status |
|---|---|---|
| 1 | 101 | active |
| 1 | 103 | active |
| 2 | 102 | dropped |
| 3 | 101 | active |
| 3 | 104 | active |
SELECT
s.name AS student_name,
c.name AS course_name,
f.name AS faculty_name
FROM students s
JOIN enrollments e
ON s.id = e.student_id
AND e.status = 'active'
JOIN courses c
ON e.course_id = c.id
AND c.name != 'PE'
JOIN faculties f
ON c.faculty_id = f.id
AND f.name IS NOT NULL;
Qui filtriamo contemporaneamente per:
- Record attivi,
- Corsi tranne "PE",
- Facoltà che hanno un nome.
Risultato della query:
| student_name | course_name | faculty_name |
|---|---|---|
| Otto Song | Math | Engineering |
| Otto Song | CS | Engineering |
| Alex Lin | Math | Engineering |
Spero che questa lezione ti sia piaciuta. Userai spesso più JOIN con filtri nelle tue query. Quasi sempre :)
GO TO FULL VERSION