Una tabella pivot trasforma le righe in colonne: una riga per gruppo e una colonna per ogni valore di un altro campo, ad esempio una colonna per trimestre. In SQL la costruisci con CASE dentro un'aggregazione come SUM, raggruppando per ciò che deve rappresentare ogni riga. Gli esempi funzionano in PostgreSQL, MySQL, SQLite e SQL Server.
Come creare una tabella pivot in SQL
Scrivi un'aggregazione per ogni colonna di output. Il CASE al suo interno fa passare solo le righe che appartengono a quella colonna, e GROUP BY restituisce una riga per gruppo:
SELECT
row_column,
SUM(CASE WHEN pivot_column = 'value 1' THEN amount ELSE 0 END) AS value_1,
SUM(CASE WHEN pivot_column = 'value 2' THEN amount ELSE 0 END) AS value_2
FROM table_name
GROUP BY row_column;
Qui ogni prodotto ha una riga, con le unità di ogni trimestre affiancate:
-- Input: sales
-- product | quarter | units
-- ---------+---------+------
-- Keyboard | Q1 | 120
-- Keyboard | Q2 | 95
-- Monitor | Q1 | 40
-- Monitor | Q2 | 55
-- Mouse | Q2 | 210
SELECT
product,
SUM(CASE WHEN quarter = 'Q1' THEN units ELSE 0 END) AS q1,
SUM(CASE WHEN quarter = 'Q2' THEN units ELSE 0 END) AS q2
FROM sales
GROUP BY product
ORDER BY product;
-- Output:
-- product | q1 | q2
-- ---------+-----+----
-- Keyboard | 120 | 95
-- Monitor | 40 | 55
-- Mouse | 0 | 210
Regole da conoscere
-
Ogni colonna la nomini tu. SQL non può creare colonne a partire dai dati, quindi un terzo trimestre richiede una terza riga
CASE. Se l'elenco dei valori cambia di continuo, costruisci la query nel codice della tua applicazione. -
ELSE 0serve solo perSUMe i conteggi. Trasforma una combinazione mancante in 0 invece di NULL. ConAVG,MINoMAX, omettiELSE, altrimenti gli zeri vengono considerati valori reali:
-- Input: the sales table above
SELECT
product,
ROUND(AVG(CASE WHEN quarter = 'Q1' THEN units ELSE 0 END), 1) AS q1_else_0,
ROUND(AVG(CASE WHEN quarter = 'Q1' THEN units END), 1) AS q1_no_else
FROM sales
GROUP BY product
ORDER BY product;
-- Output:
-- product | q1_else_0 | q1_no_else
-- ---------+-----------+-----------
-- Keyboard | 60.0 | 120.0
-- Monitor | 20.0 | 40.0
-- Mouse | 0.0 | NULL
Sintassi alternativa
Incontrerai anche FILTER in PostgreSQL e SQLite, e l'operatore PIVOT in SQL Server. Entrambi lasciano una combinazione mancante come NULL anziché 0:
-- Input: the sales table above
-- PostgreSQL and SQLite
SELECT
product,
SUM(units) FILTER (WHERE quarter = 'Q1') AS q1,
SUM(units) FILTER (WHERE quarter = 'Q2') AS q2
FROM sales
GROUP BY product
ORDER BY product;
-- SQL Server
SELECT product, [Q1] AS q1, [Q2] AS q2
FROM (SELECT product, quarter, units FROM sales) AS src
PIVOT (SUM(units) FOR quarter IN ([Q1], [Q2])) AS p
ORDER BY product;
-- Output:
-- product | q1 | q2
-- ---------+------+----
-- Keyboard | 120 | 95
-- Monitor | 40 | 55
-- Mouse | NULL | 210
Nel codice MySQL vedrai spesso SUM(IF(quarter = 'Q1', units, 0)), un modo più breve di scrivere la versione con CASE. L'estensione tablefunc di PostgreSQL offre anche la funzione crosstab(); vedi creare tabelle pivot in PostgreSQL con crosstab().
Quando conviene usare una tabella pivot?
Usa il pivot quando un elenco di righe sarebbe più leggibile come griglia. Questi sono i motivi più comuni.
Confrontare una misura fianco a fianco
Metti i valori da confrontare (giorni, trimestri, regioni) uno accanto all'altro in una riga, invece che sparsi in un lungo elenco.
Mostrare più misure per ogni colonna
Ogni colonna di output è un'aggregazione a sé, quindi puoi abbinarle: sessioni e visualizzazioni di pagina per dispositivo, oppure ordini e fatturato per canale.
Contare le righe per categoria
Per contare invece di sommare, somma 1 per ogni riga corrispondente, oppure usa COUNT(CASE WHEN ... THEN 1 END): COUNT ignora i NULL prodotti dalle altre righe. Così ottieni le attività per stato o i ticket per priorità.
Trasformare righe chiave-valore in colonne
Impostazioni e attributi dei prodotti sono spesso salvati come una riga per nome e valore. Il pivot con MAX(CASE ...) restituisce una riga per elemento con una colonna per attributo. MAX funziona sul testo e prende l'unico valore di ogni gruppo.
Esempi reali di tabelle pivot in SQL
Valutazione media delle app per piattaforma
Ometti ELSE quando calcoli una media, così un'app senza recensioni su una piattaforma mostra NULL invece di un punteggio fuorviante:
-- Input: reviews
-- app | platform | rating
-- ---------+----------+-------
-- Budgetly | iOS | 5
-- Budgetly | iOS | 4
-- Budgetly | Android | 3
-- FitTrack | Android | 4
-- FitTrack | Android | 5
-- In SQL Server, AVG of an integer column drops the decimals: use rating * 1.0
SELECT
app,
ROUND(AVG(CASE WHEN platform = 'iOS' THEN rating END), 1) AS ios,
ROUND(AVG(CASE WHEN platform = 'Android' THEN rating END), 1) AS android
FROM reviews
GROUP BY app
ORDER BY app;
-- Output:
-- app | ios | android
-- ---------+------+--------
-- Budgetly | 4.5 | 3.0
-- FitTrack | NULL | 4.5
Ordini e vendite per canale in ogni regione
Due misure per ogni canale, il numero di ordini e un totale:
-- Input: orders
-- id | region | channel | amount
-- ---+--------+---------+-------
-- 1 | North | web | 120.00
-- 2 | North | store | 80.00
-- 3 | North | web | 45.50
-- 4 | South | store | 210.00
-- 5 | South | store | 64.00
SELECT
region,
COUNT(CASE WHEN channel = 'web' THEN 1 END) AS web_orders,
SUM(CASE WHEN channel = 'web' THEN amount ELSE 0 END) AS web_sales,
COUNT(CASE WHEN channel = 'store' THEN 1 END) AS store_orders,
SUM(CASE WHEN channel = 'store' THEN amount ELSE 0 END) AS store_sales
FROM orders
GROUP BY region
ORDER BY region;
-- Output:
-- region | web_orders | web_sales | store_orders | store_sales
-- -------+------------+-----------+--------------+------------
-- North | 2 | 165.50 | 1 | 80.00
-- South | 0 | 0 | 2 | 274.00
Ticket di supporto per operatore e priorità
Un conteggio per ogni priorità, più un totale. COUNT(*) conta comunque tutte le righe del gruppo:
-- Input: tickets
-- id | agent | priority
-- ---+-------+---------
-- 1 | Priya | urgent
-- 2 | Priya | normal
-- 3 | Marco | normal
-- 4 | Priya | normal
-- 5 | Marco | low
SELECT
agent,
COUNT(CASE WHEN priority = 'low' THEN 1 END) AS low,
COUNT(CASE WHEN priority = 'normal' THEN 1 END) AS normal,
COUNT(CASE WHEN priority = 'urgent' THEN 1 END) AS urgent,
COUNT(*) AS total
FROM tickets
GROUP BY agent
ORDER BY agent;
-- Output:
-- agent | low | normal | urgent | total
-- ------+-----+--------+--------+------
-- Marco | 1 | 1 | 0 | 2
-- Priya | 0 | 2 | 1 | 3
Una riga di impostazioni per utente
Ogni impostazione è salvata come nome e valore. Un utente che non l'ha mai impostata ottiene NULL in quella colonna:
-- Input: user_settings
-- user_id | setting | value
-- --------+----------+--------------
-- 1 | theme | dark
-- 1 | language | en
-- 2 | language | de
-- 2 | timezone | Europe/Berlin
-- 3 | theme | light
SELECT
user_id,
MAX(CASE WHEN setting = 'theme' THEN value END) AS theme,
MAX(CASE WHEN setting = 'language' THEN value END) AS language,
MAX(CASE WHEN setting = 'timezone' THEN value END) AS timezone
FROM user_settings
GROUP BY user_id
ORDER BY user_id;
-- Output:
-- user_id | theme | language | timezone
-- --------+-------+----------+--------------
-- 1 | dark | en | NULL
-- 2 | NULL | de | Europe/Berlin
-- 3 | light | NULL | NULL
Approfondimenti sulle tabelle pivot
Su questo blog:
-
Come fare il pivot in PostgreSQL:
CASEecrosstab()in PostgreSQL -
Creare tabelle pivot in PostgreSQL con crosstab(): l'estensione
tablefuncnel dettaglio - Guida all'istruzione CASE in SQL: le condizioni dentro ogni colonna pivot
- Guida a GROUP BY in SQL: raggruppare le righe prima del pivot
Documentazione ufficiale:
- PostgreSQL: espressioni di aggregazione e FILTER e crosstab()
- MySQL: operatore CASE
- SQLite: funzioni di aggregazione e FILTER
- SQL Server: PIVOT e UNPIVOT
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.
Cosa dicono gli utenti di Beekeeper Studio
"Beekeeper Studio ha completamente sostituito il mio vecchio flusso di lavoro SQL. È veloce, intuitivo e rende di nuovo piacevole lavorare con i database."
"Ho provato tante GUI per database, ma Beekeeper trova l'equilibrio perfetto tra funzionalità e semplicità. Funziona e basta."