Raggruppamento di dati in SQL

Nella lezione precedente abbiamo visto le funzioni di aggregazione, come COUNT() e AVG(). Queste funzioni permettono di ottenere informazioni riassuntive a partire da più righe, come il numero totale di prodotti o il prezzo medio.

Finora abbiamo applicato queste funzioni all'insieme complessivo delle righe considerate dalla query. Ma se vogliamo ottenere un risultato separato per ogni categoria, città o altro gruppo di dati, possiamo usare la clausola GROUP BY.

GROUP BY permette di dividere le righe in gruppi in base a uno o più valori e di applicare una funzione di aggregazione a ciascun gruppo.

La clausola GROUP BY

Possiamo utilizzare GROUP BY per raggruppare in gruppi i risultati di una query.

Supponiamo di avere una tabella prodotti con le colonne nome, categoria e prezzo. Se vogliamo conoscere il numero di prodotti presenti in ogni categoria, possiamo scrivere:

SELECT categoria, COUNT(*) AS numero_prodotti
FROM prodotti
GROUP BY categoria;

La query raggrupperà i risultati in base alla categoria e conterà quanti prodotti sono presenti in ciascuna categoria. COUNT(*) viene quindi calcolato separatamente per ogni gruppo, anziché sull'intera tabella.

Possiamo fare la stessa cosa con altre funzioni di aggregazione. Per esempio, per calcolare il prezzo medio dei prodotti di ogni categoria:

SELECT categoria, AVG(prezzo) AS prezzo_medio
FROM prodotti
GROUP BY categoria;

In sintesi, la logica dell'esempio è la seguente:

  1. GROUP BY divide le righe in gruppi.
  2. La funzione di aggregazione calcola un risultato per ogni gruppo.
  3. SELECT mostra il gruppo e il risultato ottenuto.

GROUP BY senza funzione di aggregazione

GROUP BY viene normalmente utilizzato insieme alle funzioni di aggregazione. Tuttavia, può anche essere utilizzato per ottenere un risultato equivalente a un elenco di valori distinti.

Ad esempio, per restituire una riga per ogni categoria, scriviamo:

SELECT categoria FROM prodotti
GROUP BY categoria;

Quando l'obiettivo è semplicemente ottenere valori distinti, però, DISTINCT (che abbiamo visto nella lezione sulla selezione dei dati) è generalmente più chiaro:

SELECT DISTINCT categoria
FROM prodotti;

Regole di GROUP BY

Quando utilizziamo GROUP BY, è importante seguire una regola fondamentale: ogni colonna presente in SELECT che non è utilizzata all'interno di una funzione di aggregazione deve essere presente anche in GROUP BY.

Ad esempio, la query seguente non è corretta:

SELECT categoria, nome, COUNT(*)
FROM prodotti
GROUP BY categoria;

Nell'esempio, categoria è presente in GROUP BY, ma nome no. Per ogni categoria potrebbero esserci molti prodotti e quindi molti valori diversi di nome. SQL non avrebbe un valore univoco da mostrare per nome.

La query dovrebbe decidere quale nome mostrare per il gruppo PC. Non esiste una risposta univoca. Spesso MySQL sceglie il primo valore di nome del gruppo.

Per correggere il problema, possiamo raggruppare i risultati sia per categoria sia per nome:

SELECT categoria, nome, COUNT(*) AS quantita
FROM prodotti
GROUP BY categoria, nome;

Raggruppare per più colonne

GROUP BY permette di raggruppare i dati utilizzando più colonne contemporaneamente.

Per esempio, supponiamo di avere una tabella ordini con le colonne cliente_id, anno e totale. Possiamo calcolare il totale degli ordini per ogni cliente e per ogni anno:

SELECT cliente_id, anno, SUM(totale) AS totale_ordini
FROM ordini
GROUP BY cliente_id, anno;

In questo caso viene creato un gruppo per ogni combinazione di cliente_id e anno.

Raggruppare utilizzando espressioni e funzioni

È possibile utilizzare nel GROUP BY anche espressioni e funzioni, non soltanto il nome diretto di una colonna.

Supponiamo, ad esempio, di avere una colonna data_ordine e di voler raggruppare gli ordini per anno. Possiamo utilizzare YEAR() direttamente nel GROUP BY:

SELECT YEAR(data_ordine) AS anno,
       COUNT(*) AS numero_ordini
FROM ordini
GROUP BY YEAR(data_ordine);

In questo caso SQL non raggruppa per la data completa, ma per il valore restituito da YEAR(data_ordine).

Espressioni in GROUP BY

Possiamo utilizzare anche espressioni matematiche con la clausola GROUP BY.

Per esempio, supponiamo di voler raggruppare i prodotti in base a una fascia di prezzo:

SELECT
    CASE
        WHEN prezzo < 50 THEN 'Economico'
        WHEN prezzo < 200 THEN 'Medio'
        ELSE 'Costoso'
    END AS fascia,
    COUNT(*) AS numero_prodotti
FROM prodotti
GROUP BY
    CASE
        WHEN prezzo < 50 THEN 'Economico'
        WHEN prezzo < 200 THEN 'Medio'
        ELSE 'Costoso'
    END;

Qui utilizziamo CASE, che abbiamo visto nella lezione sulla logica condizionale, per creare una categoria calcolata. GROUP BY raggruppa quindi le righe in base al risultato di questa espressione.

Usare GROUP BY e WHERE

GROUP BY viene spesso utilizzato insieme a WHERE, ma le due clausole svolgono funzioni diverse. WHERE filtra le singole righe prima del raggruppamento.

Prendiamo in considerazione questo esempio per capire:

SELECT categoria, COUNT(*) AS numero_prodotti
FROM prodotti
WHERE prezzo >= 50
GROUP BY categoria;

In questo esempio:

  1. WHERE mantiene soltanto i prodotti con prezzo almeno pari a 50.
  2. GROUP BY divide le righe rimaste per categoria.
  3. COUNT(*) conta i prodotti all'interno di ogni categoria.

Prova!Completa gli spazi vuoti con il testo appropriato.
-- Raggruppa i risultati per anno
SELECT anno, COUNT(*) AS utenti
FROM utenti
anno;

Prova! Scegli l'opzione corretta tra quelle elencate.
-- Raggruppa correttamente i risultati
SELECT categoria, nome, COUNT(*) AS quantita
FROM prodotti
GROUP BY _______________;