SQL GROUP BY e Funzioni di Aggregazione: Riassumi i Tuoi Dati

Published: 2026-04-08

GROUP BY comprime più righe che condividono lo stesso valore in una o più colonne in un’unica riga di riepilogo. Combinato con funzioni di aggregazione come COUNT, SUM, AVG, MIN e MAX, è la base di quasi tutte le query di reporting e analytics. GROUP BY è supportato in tutti i database SQL, inclusi SQLite, PostgreSQL, MySQL, SQL Server e Oracle.

Dati di esempio

Usiamo due tabelle in tutti gli esempi:

Tabella employees:

id name department hire_date salary
1 Alice Engineering 2023-01-15 95000
2 Bob Engineering 2023-06-01 85000
3 Carol Engineering 2024-03-10 85000
4 Dave Sales 2023-02-20 72000
5 Eve Sales 2023-09-05 68000
6 Frank Sales 2024-01-15 65000
7 Grace Marketing 2023-04-01 82000
8 Henry Marketing 2024-06-01 70000

Tabella orders:

id customer product category quantity unit_price order_date status
1 Acme Corp Laptop Pro Electronics 3 1299.99 2026-01-05 delivered
2 Acme Corp Wireless Mouse Electronics 10 29.99 2026-01-12 delivered
3 Globex Office Chair Furniture 5 249.99 2026-01-18 delivered
4 Globex Standing Desk Furniture 2 599.99 2026-01-20 shipped
5 Initech Notebook Pack Stationery 20 12.99 2026-02-03 delivered
6 Initech Pen Set Stationery 15 8.99 2026-02-07 delivered
7 Acme Corp Monitor 27" Electronics 4 399.99 2026-02-14 delivered
8 Umbrella Ltd Laptop Pro Electronics 2 1299.99 2026-02-20 cancelled
9 Umbrella Ltd Desk Lamp Furniture 8 39.99 2026-03-01 delivered
10 Globex Wireless Mouse Electronics 12 29.99 2026-03-05 shipped
11 Initech Monitor 27" Electronics 1 399.99 2026-03-10 delivered
12 Acme Corp Standing Desk Furniture 1 599.99 2026-03-15 processing
Esplora il dataset in DbGate Lite

Sintassi di GROUP BY

SELECT column1, aggregate_function(column2)
FROM table_name
WHERE condition
GROUP BY column1
HAVING aggregate_condition
ORDER BY column1;
  • GROUP BY — una o più colonne le cui combinazioni di valori distinti definiscono ciascun gruppo
  • Funzioni di aggregazione — calcolano un singolo valore da tutte le righe del gruppo
  • HAVING — filtra i gruppi (come WHERE, ma per i risultati raggruppati)
  • Ogni colonna in SELECT che non è all’interno di un’aggregazione deve comparire in GROUP BY

Ordine di esecuzione SQL

Le clausole SQL sono scritte in un certo ordine ma eseguite in un ordine diverso. Capirlo aiuta a spiegare perché HAVING può fare riferimento a funzioni di aggregazione ma WHERE no, e perché non puoi usare un alias di SELECT in una clausola HAVING.

FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY
Step Clause Cosa succede
1 FROM Carica le tabelle e valuta eventuali JOIN
2 WHERE Filtra le singole righe — le funzioni di aggregazione non sono permesse qui
3 GROUP BY Comprimi le righe rimanenti in gruppi
4 HAVING Filtra i gruppi — le funzioni di aggregazione sono permesse qui
5 SELECT Calcola le colonne di output e gli alias
6 ORDER BY Ordina il result set finale

Per questo WHERE status = 'delivered' viene eseguito prima del raggruppamento (filtro veloce a livello di riga), mentre HAVING COUNT(*) > 2 viene eseguito dopo il raggruppamento (filtra interi gruppi).

COUNT()

COUNT(*) conta tutte le righe in un gruppo. COUNT(column) conta i valori non NULL in quella colonna.

Esempio: Numero di dipendenti per reparto

Esempio: Numero di ordini per cliente

Esempio: Conteggio ordini consegnati vs altri stati

Conta solo gli ordini che corrispondono a una condizione usando COUNT con un’espressione CASE:

SUM()

SUM(column) somma tutti i valori nel gruppo per una colonna numerica.

Esempio: Salario totale per reparto

Esempio: Fatturato totale per categoria

Calcola il fatturato come quantity * unit_price:

Esempio: Fatturato per cliente e categoria

Raggruppa per più colonne per suddividere ulteriormente il fatturato:

AVG()

AVG(column) restituisce la media aritmetica di tutti i valori non NULL nel gruppo.

Esempio: Salario medio per reparto

Esempio: Valore medio dell’ordine per cliente

MIN() e MAX()

MIN(column) e MAX(column) restituiscono rispettivamente il valore più piccolo e più grande nel gruppo.

Esempio: Intervallo di salari per reparto

Esempio: Prima e ultima data d’ordine per cliente

Combinare più aggregazioni

Puoi combinare qualsiasi numero di funzioni di aggregazione in un’unica query.

Esempio: Riepilogo completo del reparto

Esempio: Riepilogo ordini per cliente

HAVING

HAVING filtra i gruppi dopo l’aggregazione — è l’equivalente di WHERE per i risultati raggruppati.

Esempio: Reparti con più di due dipendenti

Esempio: Clienti con fatturato totale superiore a 2000

Esempio: Categorie con valore medio dell’ordine inferiore a 500

WHERE vs. HAVING

WHERE filtra le singole righe prima del raggruppamento. HAVING filtra i gruppi dopo l’aggregazione.

Esempio: Fatturato solo dagli ordini consegnati per cliente

Usa WHERE per escludere righe prima del raggruppamento, poi HAVING per filtrare i risultati:

GROUP BY con JOIN

Puoi raggruppare dati che coprono più tabelle effettuando prima una join.

Esempio: Fatturato per reparto tramite join con employees

Esempio: Conteggio ordini e fatturato per categoria e mese

GROUP BY con espressioni

Puoi raggruppare per espressioni calcolate, non solo per colonne semplici.

Esempio: Dipendenti assunti per anno

Esempio: Ordini per mese

COUNT(DISTINCT)

COUNT(DISTINCT column) conta il numero di valori unici nel gruppo.

Esempio: Prodotti unici ordinati per cliente

Filtrare gruppi con più condizioni

HAVING supporta qualsiasi espressione booleana, incluse combinazioni AND/OR.

Esempio: Clienti attivi di alto valore

Trova i clienti con più di un ordine e fatturato totale superiore a 1000:

Esempio pratico: Report di vendita

Combina tutto in un report di riepilogo vendite completo.

Esempio: Report sulle performance per categoria

Esempio: Prodotto top per fatturato

GROUP BY vs. funzioni finestra (window functions)

GROUP BY comprime le righe in una riga per gruppo. Le funzioni finestra mantengono tutte le righe e aggiungono una colonna calcolata — vedi il tutorial sulle funzioni finestra SQL per i dettagli.

Esempio: GROUP BY restituisce una riga per reparto

Esempio: La funzione finestra mantiene tutte le righe

Casi d’uso comuni per GROUP BY

  • Dashboard di reporting — Riepiloga totali, medie e conteggi per categoria o periodo di tempo
  • Analisi dell’organico — Conta dipendenti, utenti o record per gruppo
  • Suddivisione del fatturato — Totale e media del fatturato per prodotto, area geografica o cliente
  • Analisi dei trend — Raggruppa per mese o anno per individuare pattern nel tempo
  • Top-N per gruppo — Combina con subquery o CTE per trovare il migliore in ogni gruppo
  • Validazione dei dati — Conta duplicati o valori NULL tra i gruppi
  • Analisi di coorte — Raggruppa gli utenti per data di registrazione o segmento di comportamento

Suggerimenti sulle prestazioni

  1. Indicizza le colonne di GROUP BY — Gli indici sulle colonne di raggruppamento velocizzano la fase di ordinamento e grouping
  2. Filtra presto con WHERE — Ridurre le righe prima del raggruppamento con WHERE è più veloce che filtrare dopo con HAVING
  3. Evita SELECT * — Seleziona solo le colonne di cui hai davvero bisogno; le operazioni di aggregazione beneficiano di dati più “stretti”
  4. Usa indici coprenti (covering indexes) — Un indice che include sia la colonna di GROUP BY sia la colonna aggregata può evitare una scansione completa della tabella
  5. Valuta viste materializzate — Per aggregazioni calcolate spesso su tabelle grandi, alcuni database supportano riepiloghi pre-calcolati
  • GROUP BY comprime le righe che condividono gli stessi valori di colonna in una riga di riepilogo per ogni combinazione unica
  • COUNT(*) conta tutte le righe; COUNT(column) conta i valori non NULL; COUNT(DISTINCT column) conta i valori unici
  • SUM totalizza i valori numerici; AVG calcola la media; MIN / MAX trovano i valori limite
  • Ogni colonna in SELECT deve comparire in GROUP BY oppure essere racchiusa in una funzione di aggregazione
  • WHERE filtra le righe prima del raggruppamento; HAVING filtra i gruppi dopo l’aggregazione
  • Puoi fare GROUP BY su espressioni (ad es. SUBSTR(date, 1, 7) per un raggruppamento a livello di mese)
  • Effettuare join tra tabelle prima del raggruppamento ti permette di combinare dati da più fonti in un unico report
  • Usa le funzioni finestra invece di GROUP BY quando ti servono metriche a livello di gruppo senza perdere le singole righe

Prova a combinare GROUP BY con JOIN, espressioni CASE e clausole HAVING per costruire i report di cui hai bisogno per l’analisi dei dati!