Условие
Таблица sales(product_category, sale_date, revenue). Сформируйте отчёт, где строки — категории, колонки — месяцы текущего года (jan, feb, ..., dec), значения — сумма выручки. Пустые ячейки — 0.
Решение
Условная агрегация (universal)
SELECT
product_category,
SUM(CASE WHEN EXTRACT(MONTH FROM sale_date) = 1 THEN revenue ELSE 0 END) AS jan,
SUM(CASE WHEN EXTRACT(MONTH FROM sale_date) = 2 THEN revenue ELSE 0 END) AS feb,
SUM(CASE WHEN EXTRACT(MONTH FROM sale_date) = 3 THEN revenue ELSE 0 END) AS mar,
SUM(CASE WHEN EXTRACT(MONTH FROM sale_date) = 4 THEN revenue ELSE 0 END) AS apr,
SUM(CASE WHEN EXTRACT(MONTH FROM sale_date) = 5 THEN revenue ELSE 0 END) AS may,
SUM(CASE WHEN EXTRACT(MONTH FROM sale_date) = 6 THEN revenue ELSE 0 END) AS jun,
SUM(CASE WHEN EXTRACT(MONTH FROM sale_date) = 7 THEN revenue ELSE 0 END) AS jul,
SUM(CASE WHEN EXTRACT(MONTH FROM sale_date) = 8 THEN revenue ELSE 0 END) AS aug,
SUM(CASE WHEN EXTRACT(MONTH FROM sale_date) = 9 THEN revenue ELSE 0 END) AS sep,
SUM(CASE WHEN EXTRACT(MONTH FROM sale_date) = 10 THEN revenue ELSE 0 END) AS oct,
SUM(CASE WHEN EXTRACT(MONTH FROM sale_date) = 11 THEN revenue ELSE 0 END) AS nov,
SUM(CASE WHEN EXTRACT(MONTH FROM sale_date) = 12 THEN revenue ELSE 0 END) AS dec
FROM sales
WHERE EXTRACT(YEAR FROM sale_date) = EXTRACT(YEAR FROM CURRENT_DATE)
GROUP BY product_category
ORDER BY product_category;PIVOT в SQL Server / Oracle
SELECT * FROM (
SELECT product_category, EXTRACT(MONTH FROM sale_date) AS m, revenue
FROM sales
WHERE EXTRACT(YEAR FROM sale_date) = 2025
) src
PIVOT (
SUM(revenue)
FOR m IN (1 AS jan, 2 AS feb, ..., 12 AS dec)
);Postgres/MySQL не имеют синтаксиса PIVOT — только условная агрегация (или tablefunc.crosstab в Postgres).
Postgres crosstab
SELECT *
FROM crosstab(
$$ SELECT product_category, EXTRACT(MONTH FROM sale_date)::INT, SUM(revenue)
FROM sales WHERE EXTRACT(YEAR FROM sale_date) = 2025
GROUP BY 1, 2 ORDER BY 1, 2 $$,
$$ SELECT generate_series(1, 12) $$
) AS ct(product_category TEXT, jan NUMERIC, feb NUMERIC, ..., dec NUMERIC);tablefunc нужно подключить расширением.
Подводные камни
- Месяцы без продаж. Условная агрегация с
ELSE 0гарантирует нули. БезELSE 0будет NULL → COALESCE. decключевое слово. В Postgres зарезервировано как частьDECIMAL. Чаще ок, но в кавычках безопаснее:"dec".- Кросс-year смешение. Без
WHERE year = ...категории за 2023 и 2024 склеятся в одну строку — частая ошибка. - Хардкод 12 месяцев. Не масштабируется на «по неделям»/«по дням». Для динамического pivot — приложение или
dynamic SQL.
Эталонный ответ
12 SUM(CASE WHEN month = N THEN revenue ELSE 0 END) — простой и работает везде. Pivot-синтаксис только в SQL Server / Oracle / Postgres-через-extension.