Menu

GROUP BY e HAVING in SQLite: filtrare i risultati aggregati

Come GROUP BY raggruppa le righe in SQLite e come HAVING filtra quei gruppi dopo l'aggregazione, con la differenza tra WHERE e HAVING resa concreta.

Questa pagina include editor eseguibili: modifica, esegui e vedi subito l'output.

GROUP BY raggruppa le righe in contenitori

Le funzioni di aggregazione come COUNT, SUM e AVG riducono molte righe a un solo numero. GROUP BY ti permette di farlo per categoria: un numero per cliente, per mese, per stato. Ogni valore unico (o combinazione di valori) diventa una sola riga del risultato.

Tre clienti, tre righe in uscita. Le sei righe originali non ci sono più: sono state raccolte in contenitori per cliente, con COUNT(*) e SUM(amount) calcolati all'interno di ciascuno.

Il modello mentale: GROUP BY customer dice "tratta tutte le righe con lo stesso cliente come un unico gruppo". Poi gli aggregati lavorano su ogni gruppo separatamente.

Cosa puoi mettere nell'elenco della SELECT

Qui molti inciampano. Quando usi GROUP BY, ogni colonna nell'elenco della SELECT deve comparire nella clausola GROUP BY oppure stare dentro una funzione di aggregazione. Altrimenti il valore è ambiguo: da quale riga del gruppo dovrebbe arrivare?

Se scrivessi SELECT region, rep, SUM(amount) con GROUP BY region, SQLite la eseguirebbe senza protestare (è permissivo dove altri database la rifiutano), ma rep verrebbe preso a caso dal gruppo. Otterresti un nome di venditore per regione senza nessuna garanzia su quale. Non contarci: raggruppa per ogni colonna non aggregata che mostri.

HAVING filtra i gruppi dopo l'aggregazione

WHERE filtra le righe prima del raggruppamento. HAVING filtra i gruppi dopo il raggruppamento. Tutta la differenza è qui, ed è per questo che non puoi mettere COUNT(*) > 1 in una clausola WHERE: quando WHERE viene eseguita, il conteggio non esiste ancora.

Cleo ha fatto un solo ordine, quindi il suo gruppo viene scartato. Restano Ada e Boris. La condizione viene applicata al valore aggregato di ogni gruppo, non alle singole righe.

Puoi usare direttamente in HAVING gli alias di colonna definiti nella SELECT: SQLite lo permette:

Spesso è più leggibile che ripetere SUM(amount) nella clausola HAVING.

WHERE e HAVING: usale insieme

Le due clausole non si escludono. WHERE restringe le righe che partecipano al raggruppamento; HAVING restringe i gruppi che arrivano nell'output. La maggior parte delle query reali le usa entrambe.

Leggila dall'alto in basso, nell'ordine di esecuzione:

  1. WHERE status = 'paid': elimina del tutto le righe rimborsate.
  2. GROUP BY customer: raggruppa per cliente ciò che resta.
  3. SUM(amount) viene calcolata per ogni gruppo.
  4. HAVING SUM(amount) > 75: tiene solo i gruppi che superano la soglia.

Sopravvivono Boris (80 + 20 = 100) e Cleo (200). L'unico ordine pagato di Ada era di 50, che non raggiunge la soglia.

Più condizioni e più colonne di raggruppamento

HAVING accetta gli stessi operatori booleani di WHERE (AND, OR, NOT), e puoi raggruppare per più colonne per ottenere sottogruppi:

Ogni coppia (region, quarter) è un gruppo separato. La clausola HAVING richiede sia un totale sopra 100 sia almeno due vendite. Si qualificano solo ('North', 'Q1') e ('South', 'Q2').

Uno schema pratico: trovare i duplicati

Una query GROUP BY ... HAVING COUNT(*) > 1 è il modo standard per trovare valori duplicati in una colonna:

Emergono due duplicati. Da qui di solito decidi se unire gli account, aggiungere un vincolo UNIQUE o ripulire i dati, ma la query per scoprirli ha sempre la stessa forma.

HAVING senza GROUP BY

È insolito ma lecito. Senza GROUP BY, l'intero risultato viene trattato come un unico gruppo, e HAVING lo filtra nel suo insieme: ottieni tutti i valori aggregati oppure niente:

L'unica riga del risultato compare perché la somma è 160. Cambia la soglia in > 200 e la query non restituisce nessuna riga. In pratica abbinerai quasi sempre HAVING a GROUP BY, ma è bene sapere che il linguaggio non lo richiede.

Riepilogo veloce

  • GROUP BY raccoglie le righe in gruppi per chiave; gli aggregati vengono calcolati dentro ogni gruppo.
  • Ogni colonna non aggregata nella SELECT dovrebbe comparire in GROUP BY.
  • WHERE filtra le righe prima del raggruppamento; HAVING filtra i gruppi dopo.
  • Gli aggregati come COUNT(*) e SUM(...) vanno in HAVING, mai in WHERE.
  • HAVING accetta condizioni composte e può usare gli alias della SELECT.

Prossimo passo: le chiavi esterne

Aggregare una sola tabella è utile, ma la maggior parte degli schemi reali distribuisce i dati su più tabelle: gli ordini qui, i clienti là, i prodotti da un'altra parte. Le chiavi esterne sono il modo per collegare queste tabelle in modo che le relazioni restino coerenti. È il prossimo capitolo.

Domande frequenti

Che differenza c'è tra WHERE e HAVING in SQLite?

WHERE filtra le singole righe prima che vengano raggruppate. HAVING filtra i gruppi dopo l'aggregazione. Quindi WHERE amount > 100 tiene solo le righe sopra 100, mentre HAVING SUM(amount) > 100 tiene solo i gruppi il cui totale supera 100. Le funzioni di aggregazione come COUNT o SUM non sono ammesse in WHERE: per quello c'è HAVING.

Si può usare HAVING senza GROUP BY in SQLite?

Sì. Senza GROUP BY, SQLite tratta l'intero risultato come un unico gruppo, e HAVING filtra quel gruppo nel suo insieme. La query restituisce una riga oppure nessuna. In pratica è raro: di solito, se hai un HAVING, hai anche un GROUP BY che lo accompagna.

Come filtro i gruppi in base a COUNT in SQLite?

Metti l'aggregato in HAVING, non in WHERE. Per esempio, SELECT customer_id, COUNT(*) FROM orders GROUP BY customer_id HAVING COUNT(*) > 1 restituisce i clienti con più di un ordine. In SQLite puoi anche usare dentro HAVING un alias di colonna definito nella SELECT.

Illustrazione dei linguaggi di programmazione di Coddy

Impara a programmare con Coddy

INIZIA