Lavorare con i subquery è un po' come giocare a scacchi a strip: sembra tutto facile all’inizio, finché non fai una mossa sbagliata. E da dove arrivano gli errori? Da una comprensione sbagliata della sintassi, ignorando le particolarità della logica SQL o semplicemente per distrazione. In questa lezione parliamo degli errori più comuni e di come evitarli.
Errori di sintassi
I subquery richiedono attenzione alla sintassi. Virgole mancanti, parentesi o alias possono buttare giù tutta la tua query. Vediamo qualche problema tipico.
Parentesi mancanti
Le parentesi sono fondamentali quando usi i subquery. Un subquery va sempre tra parentesi tonde, e se ne manca anche solo una coppia, ti becchi un errore di sintassi.
Esempio di errore:
SELECT student_name
FROM students
WHERE student_id IN SELECT student_id FROM enrollments);
Errore:
ERROR: syntax error at or near "SELECT"
Correzione:
SELECT student_name
FROM students
WHERE student_id IN (SELECT student_id FROM enrollments);
Commento: La query dentro IN deve sempre essere tra parentesi, così SQL capisce che vuoi fare un subquery.
Mancanza di alias
Quando usi i subquery nella sezione FROM, ricordati di dargli un alias. Senza, PostgreSQL va in confusione totale.
Esempio di errore:
SELECT student_name, avg_score
FROM (SELECT student_id, AVG(score) AS avg_score FROM grades GROUP BY student_id)
WHERE avg_score > 80;
Errore:
ERROR: subquery in FROM must have an alias
Correzione:
SELECT student_name, avg_score
FROM (SELECT student_id, AVG(score) AS avg_score FROM grades GROUP BY student_id) AS subquery
WHERE avg_score > 80;
Commento: PostgreSQL vuole che ogni tabella temporanea (il risultato di un subquery in FROM) abbia un nome.
Problemi di performance
I subquery, soprattutto se scritti male, possono trasformare il tuo database in una lumaca. La performance va giù di brutto per via di calcoli inutili o mancanza di indici.
Calcoli inutili
I subquery nella sezione SELECT possono essere calcolati per ogni riga del risultato, e questo ti fa perdere un sacco di tempo.
Esempio:
SELECT student_name,
(SELECT COUNT(*) FROM enrollments WHERE enrollments.student_id = students.student_id) AS course_count
FROM students;
Se nella tabella students hai decine di migliaia di righe, questo subquery verrà eseguito da capo per ogni riga.
Ottimizzazione:
WITH course_counts AS (
SELECT student_id, COUNT(*) AS course_count
FROM enrollments
GROUP BY student_id
)
SELECT s.student_name, c.course_count
FROM students s
LEFT JOIN course_counts c ON s.student_id = c.student_id;
CTE (Common Table Expression) o i join ti permettono di evitare di ricalcolare tutto per ogni riga. Di questo parleremo meglio tra qualche livello :P
Mancanza di indici
Se usi subquery complessi nella sezione WHERE, assicurati che le colonne giuste siano indicizzate.
Esempio:
SELECT student_name
FROM students
WHERE student_id IN (SELECT student_id FROM enrollments WHERE course_id = 10);
Se la colonna student_id nella tabella enrollments non ha un indice, il subquery farà uno scan completo della tabella.
Ottimizzazione: Crea un indice:
CREATE INDEX idx_enrollments_course_id ON enrollments (course_id);
Ormai senti parlare di indici così spesso che sicuramente ti stai chiedendo cosa sono. Ma aspetta ancora qualche livello. Gli indici sono importanti, ma servono più che altro per velocizzare le query senza cambiare il codice. Non rendono una query brutta una buona, ma ti aiutano a velocizzare le query in produzione. Soprattutto se hai tabelle con milioni di righe.
Errori di logica
Gli errori di logica nei subquery sono frequenti quanto quelli di sintassi. I problemi possono venire da una cattiva comprensione di come funziona NULL, dei filtri o delle funzioni di aggregazione.
Uso sbagliato di NULL
NULL è una trappola che aspetta ogni principiante. Quando usi IN o NOT IN nei subquery, la presenza di NULL può cambiare il risultato.
Esempio di errore:
SELECT student_name
FROM students
WHERE student_id NOT IN (SELECT student_id FROM enrollments);
Se nella tabella enrollments ci sono righe con student_id = NULL, la query non restituirà nulla. Questo perché la condizione NOT IN funziona come NULL IS NOT IN.
Correzione:
SELECT student_name
FROM students
WHERE student_id NOT IN (SELECT student_id FROM enrollments WHERE student_id IS NOT NULL);
Filtra sempre i NULL se usi NOT IN.
Errori nelle condizioni di filtro
Spesso il subquery viene usato per filtri complessi, ma errori nelle condizioni possono portare a risultati completamente diversi da quelli che ti aspetti.
Esempio di errore:
SELECT student_name
FROM students
WHERE (SELECT AVG(score) FROM grades WHERE grades.student_id = students.student_id) > 80;
Se anche solo uno studente non ha voti, il subquery restituirà NULL e quello studente non verrà incluso nel risultato.
Correzione:
SELECT student_name
FROM students
WHERE COALESCE((SELECT AVG(score) FROM grades WHERE grades.student_id = students.student_id), 0) > 80;
Usa COALESCE per sostituire i NULL con valori di default.
Consigli per evitare errori
Per evitare gli errori tipici con i subquery, segui queste regole:
Usa bene parentesi e alias. Se qualcosa non va, controlla se hai chiuso tutte le parentesi e se hai messo gli alias nei subquery.
Ottimizza le query. Cerca di minimizzare l’uso dei subquery dove puoi usare JOIN, WITH o gli indici.
Attenzione ai NULL. Considera sempre che nei subquery possono esserci NULL e usa IS NOT NULL, COALESCE o simili.
Testa tutto. Testa ogni subquery da solo per essere sicuro che restituisca quello che vuoi.
Rendi il codice leggibile. Usa indentazione e alias per rendere il tuo codice più chiaro. Ricorda: tra un mese potresti non capire più cosa hai scritto!
GO TO FULL VERSION