🧚 Ascolta! Beekeeper Studio è una GUI per database veloce, moderna e open source Scarica
8 gennaio 2026 Di Matthew Rathbone

GROUP BY raccoglie in gruppi le righe che condividono un valore, così le funzioni di aggregazione come COUNT, SUM e AVG restituiscono un risultato per ogni gruppo invece di uno per l'intera tabella. Gli esempi funzionano in PostgreSQL, MySQL, SQLite e SQL Server.

Come usare GROUP BY

Indica dopo GROUP BY la colonna su cui raggruppare e racchiudi ogni altra colonna selezionata in una funzione di aggregazione: COUNT, SUM, AVG, MIN o MAX. Ottieni una riga per ogni valore distinto:

SELECT group_column, AGGREGATE(value_column)
FROM table_name
GROUP BY group_column;
-- Input: scores
-- player | team | points
-- -------+------+-------
-- Ana    | Red  | 12
-- Ben    | Blue | 9
-- Cleo   | Red  | 7
-- Dev    | Blue | 15
-- Eli    | Red  | 4

SELECT team, SUM(points) AS total_points, COUNT(*) AS players
FROM scores
GROUP BY team;

-- Output:
-- team | total_points | players
-- -----+--------------+--------
-- Red  | 23           | 3
-- Blue | 24           | 2

Cinque righe diventano due, una per squadra, e ogni aggregazione vede solo le righe di quella squadra.

Regole da conoscere

  • Ogni colonna selezionata deve stare in GROUP BY o dentro un'aggregazione. Altrimenti non c'è un singolo valore da mostrare per il gruppo e la query fallisce:
-- Input: products
-- name    | category
-- --------+---------
-- Blender | Kitchen
-- Toaster | Kitchen
-- Pillow  | Bedroom

SELECT name, category, COUNT(*) AS product_count
FROM products
GROUP BY category;

-- ERROR: column "products.name" must appear in the GROUP BY clause
--   or be used in an aggregate function
-- (SQLite runs it anyway and takes name from one row in each group)

Risolvi aggiungendo la colonna a GROUP BY, oppure aggregando anche quella, ad esempio con MIN(name).

  • I NULL formano un unico gruppo. Tutte le righe con NULL nella colonna di raggruppamento finiscono nello stesso gruppo. Usa COALESCE per dargli un'etichetta:
-- Input: products
-- name        | category
-- ------------+-----------
-- Notebook    | Stationery
-- Gift Card   | NULL
-- Pen Set     | Stationery
-- Mystery Box | NULL
-- Stapler     | Stationery

SELECT category, COUNT(*) AS product_count
FROM products
GROUP BY category;

-- Output:
-- category   | product_count
-- -----------+--------------
-- NULL       | 2
-- Stationery | 3

SELECT COALESCE(category, 'Uncategorized') AS category, COUNT(*) AS product_count
FROM products
GROUP BY category;

-- Output:
-- category      | product_count
-- --------------+--------------
-- Uncategorized | 2
-- Stationery    | 3
  • WHERE filtra le righe prima del raggruppamento, HAVING filtra i gruppi dopo. Solo HAVING può testare un'aggregazione come COUNT(*) > 2. Tutto ciò che non richiede un'aggregazione va in WHERE, così vengono raggruppate meno righe.
  • I gruppi tornano senza un ordine preciso. SQLite e SQL Server, ad esempio, elencano Blue prima di Red per la query dei punteggi qui sopra. Aggiungi ORDER BY ogni volta che l'ordine conta.

Sintassi alternativa

Puoi raggruppare per alias di colonna, o per posizione della colonna nella lista del SELECT, invece di ripetere un'espressione:

-- Input: orders
-- order_id | amount
-- ---------+-------
-- 1        | 35.00
-- 2        | 240.00
-- 3        | 18.50
-- 4        | 120.00
-- 5        | 64.00

SELECT CASE WHEN amount >= 100 THEN 'large' ELSE 'small' END AS order_size,
       COUNT(*) AS order_count
FROM orders
GROUP BY order_size  -- or GROUP BY 1
ORDER BY order_size;
-- SQL Server allows neither: repeat the CASE expression in GROUP BY

-- Output:
-- order_size | order_count
-- -----------+------------
-- large      | 2
-- small      | 3

ROLLUP aggiunge subtotali e un totale generale. Le righe di subtotale mostrano NULL nelle colonne che sommano:

-- Input: sales
-- region | category | amount
-- -------+----------+-------
-- East   | Books    | 100.00
-- East   | Toys     | 50.00
-- West   | Books    | 80.00
-- East   | Books    | 40.00

SELECT region, category, SUM(amount) AS total_sales
FROM sales
GROUP BY ROLLUP(region, category);
-- MySQL: GROUP BY region, category WITH ROLLUP
-- SQLite has no ROLLUP

-- Output:
-- region | category | total_sales
-- -------+----------+------------
-- East   | Books    | 140.00
-- East   | Toys     | 50.00
-- East   | NULL     | 190.00
-- West   | Books    | 80.00
-- West   | NULL     | 80.00
-- NULL   | NULL     | 270.00

Un GROUP BY senza aggregazione restituisce ogni valore distinto una sola volta, come SELECT DISTINCT category FROM products.

Quando usare GROUP BY?

Usa GROUP BY ogni volta che una domanda dice “per” o “per ogni”: ordini per cliente, vendite per mese, dipendenti per reparto. La maggior parte degli usi rientra in uno di questi quattro casi.

Contare le righe di ogni gruppo

Quanti ordini ci sono per ogni stato, quanti prodotti in ogni categoria, quante persone si sono iscritte ogni giorno. COUNT(*) conta tutte le righe del gruppo, mentre COUNT(column) conta solo le righe in cui quella colonna non è NULL.

Totale, media o intervallo per ogni gruppo

SUM, AVG, MIN e MAX trasformano i valori di ogni gruppo in un solo numero: fatturato per cliente, valore medio degli ordini, il prodotto più economico e quello più caro di una categoria. Una sola query può restituirne quanti ne servono.

Suddividere i risultati per più colonne

Elenca più colonne dopo GROUP BY per ottenere una riga per ogni combinazione: vendite per regione per mese, giacenze per magazzino per tipo di prodotto. Compaiono solo le combinazioni presenti nei dati.

Tenere solo i gruppi che contano

HAVING filtra i gruppi una volta formati, usando le loro aggregazioni: reparti con più di dieci persone, clienti che hanno speso più di $1,000, o valori che compaiono più di una volta, che è il modo per trovare i duplicati.

Esempi reali di GROUP BY

Contare i prodotti di ogni categoria, anche quelle vuote

GROUP BY crea gruppi solo per i valori esistenti. Per elencare anche le categorie senza prodotti, parti dalla tabella categories e fai LEFT JOIN con products. Conta una colonna della tabella unita invece di *: una categoria vuota ottiene comunque una riga, riempita di NULL, e COUNT(*) la conterebbe.

-- Input: categories
-- category_name
-- -------------
-- Audio
-- Cameras
-- Drones

-- Input: products
-- id | name       | category
-- ---+------------+---------
-- 1  | Earbuds    | Audio
-- 2  | Soundbar   | Audio
-- 3  | Action Cam | Cameras

-- Wrong: COUNT(*) counts the NULL row of an empty category
SELECT c.category_name, COUNT(*) AS product_count
FROM categories c
LEFT JOIN products p ON p.category = c.category_name
GROUP BY c.category_name
ORDER BY c.category_name;

-- Output:
-- category_name | product_count
-- --------------+--------------
-- Audio         | 2
-- Cameras       | 1
-- Drones        | 1

-- Right: COUNT(p.id) skips it
SELECT c.category_name, COUNT(p.id) AS product_count
FROM categories c
LEFT JOIN products p ON p.category = c.category_name
GROUP BY c.category_name
ORDER BY c.category_name;

-- Output:
-- category_name | product_count
-- --------------+--------------
-- Audio         | 2
-- Cameras       | 1
-- Drones        | 0

Trovare le prime tre categorie per fatturato

Somma le vendite di ogni categoria, ordina per totale e tieni le prime tre. Books arriva seconda anche se ognuna delle sue vendite è inferiore all'unica vendita di Toys:

-- Input: purchases
-- category | amount
-- ---------+-------
-- Books    | 45.00
-- Toys     | 120.00
-- Garden   | 310.00
-- Books    | 80.00
-- Music    | 25.00

SELECT category, SUM(amount) AS revenue
FROM purchases
GROUP BY category
ORDER BY revenue DESC
LIMIT 3;
-- SQL Server has no LIMIT: write SELECT TOP 3 category, ... instead

-- Output:
-- category | revenue
-- ---------+--------
-- Garden   | 310.00
-- Books    | 125.00
-- Toys     | 120.00

Report delle vendite per regione per mese

Raggruppa per regione e per mese estratto dalla data, così ogni regione ottiene una riga per mese:

-- Input: sales
-- region | sale_date  | amount
-- -------+------------+-------
-- East   | 2024-01-08 | 120.00
-- West   | 2024-01-15 | 80.00
-- East   | 2024-01-22 | 95.00
-- East   | 2024-02-03 | 60.00
-- West   | 2024-02-11 | 150.00

SELECT region, EXTRACT(MONTH FROM sale_date) AS month, SUM(amount) AS total_sales
FROM sales
GROUP BY region, EXTRACT(MONTH FROM sale_date)
ORDER BY region, month;

-- Output:
-- region | month | total_sales
-- -------+-------+------------
-- East   | 1     | 215.00
-- East   | 2     | 60.00
-- West   | 1     | 80.00
-- West   | 2     | 150.00

SQL Server e SQLite non hanno EXTRACT. Usa MONTH(sale_date) in SQL Server e strftime('%m', sale_date) in SQLite. Raggruppa anche per anno quando i tuoi dati ne coprono più di uno.

Trovare i clienti abituali di quest'anno

WHERE tiene solo gli ordini del 2024, poi HAVING tiene i clienti che ne hanno almeno due. Il cliente 202 ha due ordini in totale ma solo uno nel 2024, quindi viene escluso:

-- Input: orders
-- order_id | customer_id | order_date
-- ---------+-------------+-----------
-- 1        | 201         | 2023-11-20
-- 2        | 202         | 2023-12-05
-- 3        | 201         | 2024-01-09
-- 4        | 202         | 2024-02-14
-- 5        | 201         | 2024-03-02

SELECT customer_id, COUNT(*) AS orders_2024
FROM orders
WHERE order_date >= '2024-01-01'
GROUP BY customer_id
HAVING COUNT(*) >= 2;

-- Output:
-- customer_id | orders_2024
-- ------------+------------
-- 201         | 2

Approfondimenti su GROUP BY

Su questo blog:

Documentazione ufficiale:

Beekeeper Studio è una GUI per database gratuita & open source

Il miglior strumento per query & editor SQL che abbia mai usato. Offre tutto ciò che mi serve per gestire il mio database. - ⭐⭐⭐⭐⭐ Mit

Beekeeper Studio è veloce, intuitivo e facile da usare. Beekeeper supporta tantissimi database e funziona alla grande su Windows, Mac e Linux.

La versione Linux di Beekeeper è completa al 100%, senza tagli e senza compromessi sulle funzionalità.

Cosa dicono gli utenti di Beekeeper Studio

★★★★★
"Beekeeper Studio ha sostituito completamente il mio vecchio workflow SQL. È veloce, intuitivo e rende di nuovo piacevole lavorare con i database."
— Alex K., Database Developer
★★★★★
"Ho provato molte GUI per database, ma Beekeeper trova l'equilibrio perfetto tra funzionalità e semplicità. Funziona e basta."
— Sarah M., Full Stack Engineer

Pronto a migliorare il tuo workflow SQL?

download Scarica gratis