Короткий ответ
Нужно использовать GROUP BY ROLLUP(region, category) (или явный GROUPING SETS ((region, category), (region), ())), который за один проход выдаёт и детальные группы, и промежуточные подытоги, и общий итог, помечая «свёрнутые» измерения значением NULL.
Как это работает подробнее
Как это работает: ROLLUP(a, b) строит иерархию подытогов: сначала группы по (a, b), затем по (a) с агрегацией по всем b, затем общий итог без разбивки вообще — причём на местах «свёрнутых» столбцов в результате появляется NULL. GROUPING SETS даёт более гибкий контроль, позволяя явно указать произвольный набор нужных комбинаций группировки, не ограничиваясь строгой иерархией.
Пример кода
PostgreSQL: ROLLUP и GROUPING
SELECT region, category, SUM(amount) AS total_amount,
GROUPING(region) AS is_region_subtotal,
GROUPING(category) AS is_category_subtotal
FROM sales
GROUP BY ROLLUP(region, category);Что использовать на практике
- На практике: такой подход заменяет несколько отдельных запросов (детальный + подытог по региону + общий итог) одним, что удобно для построения сводных таблиц в отчётах, но требует аккуратной функции
GROUPING()для различения «настоящего»NULLв данных отNULL, означающего «эта строка — подытог». - Проверка: Проверка: строка с
GROUPING(region) = 1 AND GROUPING(category) = 1— это общий итог, егоtotal_amountдолжен равнятьсяSELECT SUM(amount) FROM sales.
Подводные камни
- Частые ошибки: путают строки-подытоги (с
NULLотROLLUP) со строками, гдеregionилиcategoryизначально былиNULLв данных — безGROUPING(region)эти два случая неразличимы в результате, что может привести к неверной интерпретации итоговой таблицы.