Guida completa con l'approccio delle "sottotabelle"
Per tutti gli esempi useremo questa tabella vendite (ampliata per mostrare casi complessi):
| id | venditore | categoria | prodotto | importo | quantita |
|---|---|---|---|---|---|
| 1 | Mario | Elettronica | Smartphone | 599.00 | 2 |
| 2 | Luigi | Abbigliamento | Giacca | 89.00 | 3 |
| 3 | Mario | Elettronica | Tablet | 450.00 | 1 |
| 4 | Anna | Abbigliamento | Scarpe | 120.00 | 2 |
| 5 | Luigi | Elettronica | Cuffie | 79.00 | 5 |
| 6 | Mario | Casa | Lampada | 45.00 | 4 |
| 7 | Anna | Elettronica | Mouse | 25.00 | 10 |
| 8 | Luigi | Casa | Cuscino | 35.00 | 6 |
| 9 | Mario | Abbigliamento | Maglietta | 29.00 | 8 |
| 10 | Anna | Casa | Vaso | 55.00 | 3 |
| 11 | Luigi | Elettronica | Tastiera | 65.00 | 2 |
| 12 | Mario | Elettronica | Monitor | 299.00 | 1 |
| 13 | Mario | Casa | Tappeto | 120.00 | 1 |
| 14 | Mario | Casa | Quadro | 80.00 | 2 |
| 15 | Mario | Abbigliamento | Cintura | 40.00 | 5 |
| 16 | Luigi | Abbigliamento | Sciarpa | 25.00 | 2 |
| 17 | Luigi | Abbigliamento | Guanti | 30.00 | 1 |
| 18 | Luigi | Casa | Sedia | 150.00 | 4 |
GROUP BY divide la tabella originale in tante SOTTOTABELLE,
una per ogni valore diverso della colonna specificata.
Poi esegue i calcoli (SUM, COUNT, AVG, ecc.) separatamente su ogni sottotabella.
Quando scriviamo GROUP BY venditore, SQL prende la tabella e la "spezza" in sottotabelle:
| 👤 Sottotabella MARIO | |
| venditore | importo |
|---|---|
| Mario | 599.00 |
| Mario | 450.00 |
| Mario | 45.00 |
| Mario | 29.00 |
| Mario | 299.00 |
| Mario | 120.00 |
| Mario | 80.00 |
| Mario | 40.00 |
|
SUM = 1662.00 COUNT = 8 | |
| 👤 Sottotabella LUIGI | |
| venditore | importo |
|---|---|
| Luigi | 89.00 |
| Luigi | 79.00 |
| Luigi | 35.00 |
| Luigi | 65.00 |
| Luigi | 25.00 |
| Luigi | 30.00 |
| Luigi | 150.00 |
|
SUM = 473.00 COUNT = 7 | |
| 👤 Sottotabella ANNA | |
| venditore | importo |
|---|---|
| Anna | 120.00 |
| Anna | 25.00 |
| Anna | 55.00 |
|
SUM = 200.00 COUNT = 3 | |
| venditore | totale | num_vendite |
|---|---|---|
| Mario | 1662.00 | 8 |
| Luigi | 473.00 | 7 |
| Anna | 200.00 | 3 |
Domanda: "Quanto abbiamo venduto per ogni categoria?"
SELECT categoria, SUM(importo) AS totale, COUNT(*) AS num_vendite
FROM vendite
GROUP BY categoria;
| categoria | totale | num_vendite |
|---|---|---|
| Elettronica | 1517.00 | 6 |
| Abbigliamento | 333.00 | 6 |
| Casa | 485.00 | 6 |
Domanda: "Quanto ha venduto ogni venditore in ogni categoria?"
Qui le cose si fanno interessanti. Noi dobbiamo dividere la tabella in sottotabelle, una per ogni venditore e dopodichè ogni sottotabella va divisa in gruppi di righe, una per ogni categoria. E questo si ottiene con GROUP BY Venditore, Categoria
Per visualizzarlo facilmente: immagina di avere una tabella per ogni venditore (Mario, Luigi, Anna) e, dentro questa tabella, le vendite vengono suddivise per colore in base alla categoria.
SELECT venditore, categoria, SUM(importo) AS totale
FROM vendite
GROUP BY venditore, categoria
ORDER BY venditore, categoria;
Ogni blocco di colore uniforme verrà collassato in UNA sola riga nel risultato.
| 👤 Sottotabella MARIO | |
| Prodotto | Importo |
|---|---|
| Smartphone | 599.00 |
| Tablet | 450.00 |
| Monitor | 299.00 |
| Maglietta | 29.00 |
| Cintura | 40.00 |
| Lampada | 45.00 |
| Tappeto | 120.00 |
| Quadro | 80.00 |
| 👤 Sottotabella LUIGI | |
| Prodotto | Importo |
|---|---|
| Cuffie | 79.00 |
| Tastiera | 65.00 |
| Giacca | 89.00 |
| Sciarpa | 25.00 |
| Guanti | 30.00 |
| Cuscino | 35.00 |
| Sedia | 150.00 |
| venditore | categoria | totale |
|---|---|---|
| Anna | Abbigliamento | 120.00 |
| Anna | Casa | 55.00 |
| Anna | Elettronica | 25.00 |
| Luigi | Abbigliamento | 144.00 |
| Luigi | Casa | 185.00 |
| Luigi | Elettronica | 144.00 |
| Mario | Abbigliamento | 69.00 |
| Mario | Casa | 245.00 |
| Mario | Elettronica | 1348.00 |
HAVING è una condizione che FILTRA le sottotabelle.
Dopo che GROUP BY ha creato le sottotabelle e calcolato i risultati,
HAVING decide quali sottotabelle includere nel risultato finale
e quali escludere.
Domanda: "Mostrami solo i venditori che hanno venduto più di 1000€ in totale"
SELECT venditore, SUM(importo) AS totale
FROM vendite
GROUP BY venditore
HAVING SUM(importo) > 1000;
| 👤 MARIO | |
|
SUM = 1662.00 1662 > 1000 ✅ |
| 👤 LUIGI | |
|
SUM = 473.00 473 < 1000 ❌ |
| 👤 ANNA | |
|
SUM = 200.00 200 < 1000 ❌ |
| venditore | totale |
|---|---|
| Mario | 1662.00 |
Solo la sottotabella di Mario ha superato la condizione HAVING!
Filtra le RIGHE
prima di creare le sottotabelle
Filtra le SOTTOTABELLE
dopo averle create
Domanda: "Per le vendite sopra i 50€, mostrami i venditori con almeno 2 vendite"
SELECT venditore, COUNT(*) AS vendite, SUM(importo) AS totale
FROM vendite
WHERE importo > 50 -- Filtra RIGHE prima del grouping
GROUP BY venditore
HAVING COUNT(*) >= 2; -- Filtra SOTTOTABELLE dopo il grouping
Prima vengono escluse le righe con importo ≤ 50.
Le righe rimanenti vengono divise in sottotabelle per venditore.
Vengono incluse solo le sottotabelle con almeno 2 righe valide.
| venditore | vendite | totale |
|---|---|---|
| Mario | 5 | 1593.00 |
| Luigi | 4 | 383.00 |
| Anna | 2 | 175.00 |
Spesso si fa confusione perché l'ordine in cui SCRIVI i comandi è diverso dall'ordine in cui SQL li ESEGUE.
Hai notato? Anche se SELECT è la prima parola che scrivi, logicamente è la quinta operazione eseguita!
Ecco perché SELECT è quasi alla fine: il database deve prima trovare i dati (FROM), filtrarli (WHERE), raggrupparli (GROUP BY) e filtrare i gruppi (HAVING). Solo a quel punto decide quali colonne mostrarti e calcola i risultati finali.
E infine, solo dopo aver "selezionato" i dati finali, può ordinarli (ORDER BY).
Domanda: "Mostra i venditori con più di 250€ venduti, ordinati dal migliore"
SELECT venditore, COUNT(*) AS vendite, SUM(importo) AS totale, ROUND(AVG(importo), 2) AS media
FROM vendite
GROUP BY venditore
HAVING SUM(importo) > 250
ORDER BY totale DESC;
| venditore | vendite | totale | media |
|---|---|---|---|
| Mario | 8 | 1662.00 | 207.75 |
| Luigi | 7 | 473.00 | 67.57 |
Domanda: "Trova le combinazioni venditore+categoria con totale > 100€"
SELECT venditore, categoria, SUM(importo) AS totale, SUM(quantita) AS pezzi
FROM vendite
GROUP BY venditore, categoria
HAVING SUM(importo) > 100
ORDER BY totale DESC;
| venditore | categoria | totale | pezzi |
|---|---|---|---|
| Mario | Elettronica | 1348.00 | 4 |
| Mario | Casa | 245.00 | 7 |
| Luigi | Casa | 185.00 | 10 |
| Luigi | Abbigliamento | 144.00 | 6 |
| Luigi | Elettronica | 144.00 | 7 |
| Anna | Abbigliamento | 120.00 | 2 |
SELECT venditore, prodotto, -- ❌ Errore! SUM(importo)
FROM vendite
GROUP BY venditore;
"prodotto" non è nel GROUP BY. In una sottotabella di Mario ci sono 8 prodotti diversi: quale mostrare?
SELECT venditore, SUM(quantita) AS pezzi
FROM vendite
GROUP BY venditore
HAVING SUM(quantita) > 15;
Nel SELECT Solo colonne del GROUP BY o funzioni aggregate
Conta quante vendite sono state fatte per ogni categoria.
SELECT categoria, COUNT(*) AS num_vendite
FROM vendite
GROUP BY categoria;
Trova i venditori che hanno venduto più di 15 pezzi totali.
SELECT venditore, SUM(quantita) AS pezzi
FROM vendite
GROUP BY venditore
HAVING SUM(quantita) > 15;
Per le vendite di importo superiore a 40€, trova le categorie con almeno 2 vendite.
SELECT categoria, COUNT(*) AS vendite
FROM vendite
WHERE importo > 40
GROUP BY categoria
HAVING COUNT(*) >= 2;
Trova le combinazioni venditore+categoria dove la media per vendita supera la media generale.
SELECT venditore, categoria, AVG(importo) AS media
FROM vendite
GROUP BY venditore, categoria
HAVING AVG(importo) > (SELECT AVG(importo) FROM vendite);
Divide la tabella in SOTTOTABELLE
(una per ogni valore unico della colonna)
e calcola le funzioni aggregate su ciascuna
Filtra le SOTTOTABELLE
Include nel risultato solo le sottotabelle
che soddisfano la condizione specificata
-- 1. WHERE filtra le RIGHE della tabella originale
-- 2. GROUP BY crea le SOTTOTABELLE
-- 3. HAVING decide quali SOTTOTABELLE tenere