PIVOT SQL: как превратить строки в столбцы
PIVOT SQL используют, когда значения из строк нужно разложить по отдельным колонкам. Разберём, как собрать такую сводную таблицу через условную агрегацию и FILTER.
Обновлено · Команда SQL Arena
Что потренируете
- Разворачивать категории из строк в отдельные столбцы
- Использовать SUM с FILTER в PostgreSQL
- Строить тот же pivot через SUM(CASE ...)
- Осознанно выбирать между NULL и 0
- Возвращать широкую таблицу в длинный формат через UNION ALL
Пример задачи в тренажёре
Так выглядит задача в SQL Arena: условие, редактор, результат и схема базы.
Для каждого магазина выведите shop_name, сумму продаж через web в web_sales и сумму продаж в магазине в store_sales. Магазины без продаж нужного типа тоже должны остаться в результате, а отсутствующая сумма должна отображаться как 0.
SELECT s.shop_name, COALESCE(SUM(sa.amount) FILTER (WHERE sa.channel = 'web'), 0) AS web_sales, COALESCE(SUM(sa.amount) FILTER (WHERE sa.channel = 'store'), 0) AS store_sales FROM shops s LEFT JOIN sales sa ON sa.shop_id = s.id GROUP BY s.id, s.shop_name ORDER BY s.shop_name;
| # | shop_name | web_sales | store_sales |
|---|---|---|---|
| 1 | Central | 90 | 110 |
| 2 | Harbor | 0 | 150 |
| 3 | Market | 0 | 0 |
| 4 | North | 120 | 80 |
Для каждого магазина выведите shop_name, сумму продаж через web в web_sales и сумму продаж в магазине в store_sales. Магазины без продаж нужного типа тоже должны остаться в результате, а отсутствующая сумма должна отображаться как 0.
SELECT s.shop_name, COALESCE(SUM(sa.amount) FILTER (WHERE sa.channel = 'web'), 0) AS web_sales, COALESCE(SUM(sa.amount) FILTER (WHERE sa.channel = 'store'), 0) AS store_sales FROM shops s LEFT JOIN sales sa ON sa.shop_id = s.id GROUP BY s.id, s.shop_name ORDER BY s.shop_name;
Как работает PIVOT SQL на примере
Исходная таблица sales хранит канал в строках: web, store или partner. В итоговой сводной таблице web и store становятся отдельными колонками. PostgreSQL позволяет сделать это через агрегат с FILTER: SUM(amount) считает сумму, а FILTER ограничивает конкретный агрегат только строками нужного канала. Поэтому один GROUP BY создаёт сразу несколько показателей для каждой группы.
LEFT JOIN здесь важен не как тема сам по себе, а как способ сохранить магазины, для которых подходящих продаж нет. После группировки SUM по отсутствующим строкам возвращает NULL. COALESCE(..., 0) заменяет его на ноль, потому что в этой задаче ноль означает понятный бизнес-факт: продаж по каналу не было. Без COALESCE у Harbor был бы NULL в web_sales, а у Market — NULL в обеих колонках.
Тот же pivot можно написать переносимым способом: SUM(CASE WHEN channel = 'web' THEN amount ELSE 0 END). Такой вариант полезен в диалектах, где синтаксис FILTER недоступен или нежелателен. Но принцип одинаков: набор будущих колонок известен заранее, и для каждой категории создаётся отдельное условное агрегатное выражение.
SELECT s.shop_name, COALESCE(SUM(sa.amount) FILTER (WHERE sa.channel = 'web'), 0) AS web_sales, COALESCE(SUM(sa.amount) FILTER (WHERE sa.channel = 'store'), 0) AS store_sales FROM shops s LEFT JOIN sales sa ON sa.shop_id = s.id GROUP BY s.id, s.shop_name ORDER BY s.shop_name;
| shop_name | web_sales | store_sales |
|---|---|---|
| Central | 90 | 110 |
| Harbor | 0 | 150 |
| Market | 0 | 0 |
| North | 120 | 80 |
| Park | 70 | 0 |
Ошибки при развороте строк в столбцы в SQL
Забыть ELSE 0 в CASE
В конструкции SUM(CASE WHEN ... THEN amount END) строки другой категории превращаются в NULL. Иногда это нормально, но при отсутствии совпадений весь SUM может остаться NULL. Если по смыслу нужен ноль, задайте ELSE 0 или используйте COALESCE вокруг агрегата.
Пытаться сделать динамические колонки обычным SELECT
SUM(CASE...) и FILTER хорошо работают, когда список будущих колонок заранее известен: например, web и store. Если категории появляются динамически и каждая должна становиться новой колонкой, статического SQL уже недостаточно — потребуется генерация запроса или другой формат результата.
Смешивать отсутствующее значение и ноль
NULL и 0 не всегда означают одно и то же. Ноль может означать, что показатель действительно измерен и равен нулю, а NULL — что данных нет или показатель неприменим. Перед COALESCE стоит определить, какое значение соответствует смыслу отчёта.
Задачи по теме в тренажёре
Трек «SQL с нуля → Аналитик»После простого pivot удобно закрепить приём на прикладных сводках: посчитать итоговый баланс по счетам, разнести платежи по способам оплаты или собрать показатели доставки по магазинам. Несколько таких Easy-задач доступны бесплатно. Если хочется пройти путь последовательно от базовых конструкций к аналитическим задачам, продолжайте в треке «SQL с нуля → Аналитик».
- EasyбесплатноИтоговый баланс по счетамpostgresqlmysqlclickhouse
- EasyбесплатноПлатежи через Apple Pay и другие способы оплатыpostgresqlmysqlclickhouse
- EasyбесплатноДоля мгновенных доставокpostgresqlmysqlclickhouse
- EasyбесплатноСводка доставки по магазинамpostgresql
- MediumPremiumАктивность каждого покупателяpostgresql
- MediumPremiumДоля пациентов в палате CARD-Apostgresqlmysqlclickhouse
- MediumPremiumПроцент пациентов, родившихся в 2000 годуpostgresqlmysqlclickhouse
- MediumPremiumУченики с успешностью выше 50%postgresqlmysqlclickhouse
Справочник по теме
Подробные статьи с синтаксисом и примерами в справочнике SQL Arena.
Частые вопросы
Что такое PIVOT SQL?
PIVOT SQL — это преобразование, при котором значения из строк становятся отдельными столбцами результата. Обычно такой приём применяют для отчётов и сводных таблиц, где для каждой группы нужно вывести несколько категорий рядом.
Как сделать PIVOT в SQL?
Способ зависит от СУБД. В диалектах без отдельного оператора PIVOT строки можно развернуть через условную агрегацию: SUM или COUNT сочетают с FILTER либо CASE для каждой будущей колонки.
Как превратить строки в столбцы в SQL?
Нужно определить значения, которые станут колонками, сгруппировать данные по оставшимся полям и вычислить отдельный агрегат для каждого значения. Например, суммы продаж по каналам можно вывести отдельными колонками web и store.
Чем PIVOT отличается от GROUP BY в SQL?
GROUP BY объединяет строки в группы и позволяет считать агрегаты, но сам по себе не превращает значения категорий в отдельные колонки. PIVOT или условная агрегация используют GROUP BY как часть решения, добавляя отдельное выражение для каждой выводимой категории.
Попрактикуйтесь с PIVOT SQL на задачах
В SQL Arena — 1 192 задачи с автопроверкой. Для PostgreSQL, MySQL, ClickHouse и BigQuery доступны отдельные песочницы, ER-диаграммы и EXPLAIN. Бесплатно без карты можно решать 292 задачи на всех четырёх диалектах.
Решать задачи в SQL Arena