Immagina di lavorare come analyst in un'università (stiamo facendo un database per l'università, ti ricordi?). Ti hanno chiesto non solo di mostrare gli studenti e i loro voti, ma anche di aggiungere una colonna con il voto massimo nel gruppo, così puoi confrontare facilmente i risultati. Come risolvi questa cosa? Ovviamente, usando le subquery dentro SELECT!
Una subquery in SELECT ti permette di calcolare valori direttamente mentre la query principale gira. È una figata, perché puoi combinare calcoli aggregati, filtri complessi e anche altre collezioni di dati in una sola query.
Basi delle query annidate in SELECT
Una subquery in SELECT funziona proprio come sembra: inserisci il risultato di una SELECT dentro un'altra. Così puoi calcolare valori extra per ogni riga del risultato.
Ecco un esempio semplice. Supponiamo di avere una tabella students con questa struttura:
| student_id | name | group_id |
|---|---|---|
| 1 | Linda | 101 |
| 2 | Otto | 102 |
| 3 | Anna | 101 |
E la tabella grades:
| grade_id | student_id | grade |
|---|---|---|
| 1 | 1 | 5 |
| 2 | 1 | 4 |
| 3 | 2 | 3 |
| 4 | 3 | 5 |
| 5 | 3 | 4 |
Esempio 1: Aggiungere il voto massimo nel gruppo
Obiettivo: mostrare i nomi degli studenti, i loro voti e il voto massimo nel gruppo, così si vede subito quanto i voti di ogni studente sono diversi dai migliori risultati del gruppo.
Codice SQL:
SELECT
s.name AS student_name,
g.grade AS student_grade,
(
SELECT MAX(grade) -- questa query restituisce un solo valore
FROM grades
INNER JOIN students ON grades.student_id = students.student_id
WHERE students.group_id = s.group_id
) AS max_group_grade
FROM
students s
INNER JOIN
grades g ON s.student_id = g.student_id;
Cosa succede qui:
- Per ogni studente prendiamo il suo nome e il suo voto (
s.name,g.grade). SELECT MAX(grade)— questa è la subquery che restituisce il voto massimo dentro il gruppo dello studente.- La subquery viene eseguita per ogni riga della query principale e usa la condizione
WHERE students.group_id = s.group_idper limitare la selezione a un solo gruppo.
Esempio 2: Voto medio per gruppo
Vuoi essere ancora più utile per gli analyst? Aggiungiamo all'output non solo il voto massimo, ma anche il voto medio del gruppo.
Codice SQL:
SELECT
s.name AS student_name,
g.grade AS student_grade,
(
SELECT AVG(grade)
FROM grades
INNER JOIN students ON grades.student_id = students.student_id
WHERE students.group_id = s.group_id
) AS avg_group_grade
FROM
students s
INNER JOIN
grades g ON s.student_id = g.student_id;
Adesso:
- Invece di
MAX()usiamoAVG()per calcolare il voto medio nel gruppo. - Otteniamo un'analisi "live" dei dati.
Limitazioni e consigli
Le subquery in SELECT sono potenti, ma vanno usate con attenzione:
- Performance. Ogni subquery viene eseguita per ogni riga della query principale. Questo può rallentare la query SQL se le tabelle sono grandi. Tipo, se hai 1000 studenti, la subquery gira 1000 volte!
- Indici. Per velocizzare queste query, è importante indicizzare bene le colonne usate nelle condizioni
WHEREdella subquery. - Leggibilità. Cerca di evitare troppa annidazione. Se le subquery diventano troppo complesse, pensa a spostarle in
FROMo a creare tabelle temporanee.
Esempi di utilizzo
Dai, vediamo ancora qualche caso interessante.
Esempio 3: Numero di corsi per ogni studente
Mostriamo una tabella dove per ogni studente si vede il numero di corsi a cui è iscritto. La tabella enrollments è collegata agli studenti tramite student_id:
| student_id | course_id |
|---|---|
| 1 | 201 |
| 1 | 202 |
| 2 | 201 |
| 3 | 203 |
Codice SQL:
SELECT
s.name AS student_name,
(
SELECT COUNT(*)
FROM enrollments
WHERE enrollments.student_id = s.student_id
) AS course_count
FROM
students s;
Qui la subquery conta il numero di record nella tabella enrollments per ogni studente.
Esempio 4: Flag "eccellente" per ogni studente
Vediamo se uno studente è eccellente. Diciamo che il criterio per essere "eccellente" è avere tutti i voti pari a 5.
Codice SQL:
SELECT
s.name AS student_name,
(
SELECT CASE
WHEN MIN(g.grade) = 5 THEN 'Eccellente'
ELSE 'Non eccellente'
END
FROM grades g
WHERE g.student_id = s.student_id
) AS status
FROM
students s;
Qui si usa una CASE annidata per assegnare lo status "Eccellente" solo agli studenti che hanno tutti i voti pari a 5.
Ottimizzazione delle subquery in SELECT
Abbiamo già detto che la performance può essere un problema. Ecco qualche dritta per migliorarla:
- Usa gli indici. Se le subquery filtrano dati, assicurati che le colonne usate abbiano un indice.
- Fai cache dei risultati. A volte conviene spostare le subquery in una
VIEWo in tabelle temporanee. - Meno annidamento. Non esagerare con le subquery annidate, se puoi usa un approccio più semplice.
Le subquery in SELECT aprono un sacco di possibilità per calcoli e analisi dei dati. Anche se possono essere pesanti, se le ottimizzi bene rendono SQL molto più espressivo e flessibile. Quindi non aver paura di sperimentare e trovare i tuoi modi per migliorare le query!
GO TO FULL VERSION