Как сделать PIVOT в SQL
Карьерник — квиз-тренажёр в Telegram с 1500+ вопросами для собесов аналитика. SQL, Python, A/B, метрики. Бесплатно.
Зачем это знать
Менеджер просит: «сделай отчёт по продажам — строки месяцы, столбцы продукты». В SQL данные лежат long-format (один столбец — продукт, один — сумма), а надо wide. Это и есть PIVOT.
На собесах часто дают задачу «разверните таблицу». Без PIVOT — много повторений. С native PIVOT — красиво, но не во всех БД. Универсальный способ — CASE WHEN + GROUP BY.
Исходные данные
| month | product | sales |
|---------|---------|-------|
| 2026-01 | A | 100 |
| 2026-01 | B | 200 |
| 2026-02 | A | 150 |
| 2026-02 | B | 300 |Нужно:
| month | A | B |
|---------|------|------|
| 2026-01 | 100 | 200 |
| 2026-02 | 150 | 300 |Способ 1: CASE WHEN (универсальный)
SELECT
month,
SUM(CASE WHEN product = 'A' THEN sales END) AS a,
SUM(CASE WHEN product = 'B' THEN sales END) AS b
FROM sales
GROUP BY month;Работает во всех СУБД. Минус: column list захардкожен.
Способ 2: PIVOT (MSSQL, Oracle)
SELECT * FROM sales
PIVOT (
SUM(sales) FOR product IN ([A], [B])
) AS pvt;Чище, но ограничен по диалектам.
Способ 3: BigQuery PIVOT
SELECT * FROM sales
PIVOT (SUM(sales) FOR product IN ('A', 'B'));Динамический PIVOT
Если список колонок неизвестен — нужно dynamic SQL:
-- MSSQL
DECLARE @cols NVARCHAR(MAX);
SELECT @cols = STRING_AGG(QUOTENAME(product), ',')
FROM (SELECT DISTINCT product FROM sales) p;
DECLARE @sql NVARCHAR(MAX) = '
SELECT * FROM sales
PIVOT (SUM(sales) FOR product IN (' + @cols + ')) AS pvt';
EXEC sp_executesql @sql;На практике — обычно проще сделать это в Python/BI-tool.
UNPIVOT (обратная операция)
Из wide в long:
-- MSSQL
SELECT month, product, sales
FROM wide_sales
UNPIVOT (sales FOR product IN (A, B)) AS u;В Postgres — через UNION ALL или UNNEST.
PIVOT в ClickHouse
Через arrayJoin или groupArray:
SELECT month,
sumIf(sales, product = 'A') AS a,
sumIf(sales, product = 'B') AS b
FROM sales
GROUP BY month;sumIf элегантнее CASE WHEN.
На собесе
«Разверни таблицу sales так, чтобы строки — месяцы, столбцы — продукты».
Идеальный ответ: CASE WHEN + GROUP BY. Если спросят «а если динамический список» — упомянуть dynamic SQL или BI-tool.
Частые ошибки
SUM vs MAX
Используйте SUM если может быть несколько значений. MAX — если ровно одно значение на ключ (иначе потеряете данные).
Missing values = NULL
CASE WHEN ... THEN x без ELSE даёт NULL для не-матчей. SUM(NULL) ok.
Слишком много колонок
Если результат 100 колонок — pivot не нужен. Оставьте long format + BI-tool.
Связанные темы
FAQ
Native PIVOT в Postgres?
Нет. Используйте CASE WHEN или crosstab() в tablefunc extension.
PIVOT замедляет?
Нет. Это просто GROUP BY с conditional aggregation.
Когда не нужен PIVOT?
Если можно сделать в BI (Tableau, Metabase) — лучше там. SQL pivot — для ETL.
Тренируйте SQL — откройте тренажёр с 1500+ вопросами для собесов.