Immagina di lavorare come cameriere (o barista, se ami il caffè) in un grande ristorante. Ogni giorno fai il totale delle mance che hai guadagnato. Ma c’è una particolarità: il ristorante è diviso in zone e ti interessa sapere quante mance sono state guadagnate in ogni zona separatamente. PARTITION BY è quello che SQL usa per "dividere il ristorante in zone".
Più formalmente, PARTITION BY viene usato nelle funzioni window per suddividere tutte le righe della tabella in gruppi separati (o "partizioni"). Dentro ogni gruppo la funzione window viene eseguita da capo. È come se applicassi la funzione separatamente in ogni "partizione".
Esempio: come funziona
Supponiamo di avere una tabella sales con i dati delle vendite:
| region | salesperson | amount |
|---|---|---|
| North | Alice | 100 |
| North | Bob | 200 |
| South | Alice | 150 |
| South | Charlie | 250 |
Se vogliamo calcolare quanti soldi ha guadagnato ogni venditore, ma separatamente per ogni regione, PARTITION BY è quello che ci serve.
Sintassi di PARTITION BY
La sintassi è abbastanza semplice:
window_function() OVER (PARTITION BY colonna_o_colonne)
window_function()— tipoSUM(),AVG(),ROW_NUMBER()e così via.PARTITION BY colonna— indica su quale colonna dividere le righe.OVER()— è l’operatore che dice a SQL: "Fai qualcosa all’interno della finestra specificata".
Esempio: somma per gruppi
Calcoliamo la somma delle vendite per ogni regione:
SELECT
region,
salesperson,
amount,
SUM(amount) OVER (PARTITION BY region) AS total_sales_by_region
FROM sales;
Il risultato sarà così:
| region | salesperson | amount | total_sales_by_region |
|---|---|---|---|
| North | Alice | 100 | 300 |
| North | Bob | 200 | 300 |
| South | Alice | 150 | 400 |
| South | Charlie | 250 | 400 |
Cosa succede? SQL divide le righe in gruppi in base al valore della colonna region (North e South), poi applica la funzione SUM() separatamente per ogni gruppo. Come risultato, le righe dentro il gruppo "North" ricevono lo stesso valore di somma, e le righe dentro "South" un altro.
Esempi di uso di PARTITION BY
Vediamo come PARTITION BY può essere utile in situazioni reali.
Esempio 1: Ranking dentro il gruppo
Supponiamo di voler fare il ranking dei venditori dentro ogni regione in base alle vendite. Per questo si può usare la combinazione di PARTITION BY e la funzione RANK():
SELECT
region,
salesperson,
amount,
RANK() OVER (PARTITION BY region ORDER BY amount DESC) AS rank_in_region
FROM sales;
Risultato:
| region | salesperson | amount | rank_in_region |
|---|---|---|---|
| North | Bob | 200 | 1 |
| North | Alice | 100 | 2 |
| South | Charlie | 250 | 1 |
| South | Alice | 150 | 2 |
La funzione RANK() assegna un rank dentro ogni gruppo region, partendo da 1. Nota che per ogni gruppo i rank partono da uno.
Esempio 2: Confronto di ogni valore con la media del gruppo
Supponiamo di voler vedere quanto ogni venditore ha guadagnato rispetto alla media della sua regione. Usiamo AVG():
SELECT
region,
salesperson,
amount,
AVG(amount) OVER (PARTITION BY region) AS avg_sales_by_region,
amount - AVG(amount) OVER (PARTITION BY region) AS diff_from_avg
FROM sales;
Risultato:
| region | salesperson | amount | avg_sales_by_region | diff_from_avg |
|---|---|---|---|---|
| North | Alice | 100 | 150 | -50 |
| North | Bob | 200 | 150 | 50 |
| South | Alice | 150 | 200 | -50 |
| South | Charlie | 250 | 200 | 50 |
Prima SQL divide le righe in gruppi per region. Poi calcola il valore medio AVG(amount) per ogni gruppo. Infine, per ogni riga calcola la differenza tra il suo valore e la media.
Esempio 3: Numerazione delle righe dentro il gruppo
Mettiamo che vuoi numerare tutte le transazioni dentro ogni gruppo regione. Usiamo ROW_NUMBER():
SELECT
region,
salesperson,
amount,
ROW_NUMBER() OVER (PARTITION BY region ORDER BY amount DESC) AS row_number
FROM sales;
Risultato:
| region | salesperson | amount | row_number |
|---|---|---|---|
| North | Bob | 200 | 1 |
| North | Alice | 100 | 2 |
| South | Charlie | 250 | 1 |
| South | Alice | 150 | 2 |
Confronto con GROUP BY
Spesso c’è confusione tra PARTITION BY e GROUP BY. Vediamo le differenze:
GROUP BY
GROUP BY cambia la struttura del risultato — trasforma le righe della tabella in aggregati. Per esempio:
SELECT
region,
SUM(amount) AS total_sales
FROM sales
GROUP BY region;
Risultato:
| region | total_sales |
|---|---|
| North | 300 |
| South | 400 |
Qui perdiamo le info sui venditori, perché i dati vengono aggregati.
PARTITION BY
PARTITION BY, invece, non cambia la struttura. Vediamo ancora ogni riga, ma abbiamo valori extra calcolati per gruppo. Quindi, PARTITION BY permette di aggregare senza perdere i dettagli.
Errori comuni con PARTITION BY
Errore 1: Dimenticato PARTITION BY
A volte vuoi raggruppare i dati ma ti dimentichi di usare PARTITION BY. Per esempio:
SELECT
region,
salesperson,
amount,
SUM(amount) OVER () AS total_sales
FROM sales;
Risultato:
| region | salesperson | amount | total_sales |
|---|---|---|---|
| North | Alice | 100 | 700 |
| North | Bob | 200 | 700 |
| South | Alice | 150 | 700 |
| South | Charlie | 250 | 700 |
Qui SUM(amount) è calcolata per tutta la tabella, non separatamente per ogni regione. Se vuoi considerare le regioni, non dimenticare di mettere PARTITION BY region.
Errore 2: Ordine sbagliato in ORDER BY
L’ordine delle righe nella finestra è importante per funzioni come RANK() o ROW_NUMBER(). Fai attenzione quando usi ORDER BY dentro OVER().
GO TO FULL VERSION