Для отчёта по двум измерениям нужны итоги по каждой комбинации измерений, по каждому измерению отдельно и о...

Для отчёта по двум измерениям нужны итоги по каждой комбинации измерений, по каждому измерению отдельно и общий итог. Какой механизм группировки выражает это требование напрямую?

Проходите собеседования с ИИ помощником Hintsage

Краткий ответ

Для этого используется CUBE. Он строит группы для всех комбинаций указанных измерений, включая детальные комбинации, промежуточные итоги по каждому измерению и общий итог.

Исторический контекст

Обычный GROUP BY решает задачу одной гранулярности: например, считает показатели только по паре «регион — год». Для аналитических отчётов потребовались несколько уровней агрегации в одном результате, без ручного объединения множества запросов через UNION ALL.

Механизмы расширенной группировки, включая ROLLUP, CUBE и GROUPING SETS, позволяют описывать структуру итогов декларативно. Это уменьшает дублирование логики агрегации и снижает риск расхождения формул между отдельными запросами.

Постановка проблемы

Если отдельно считать итоги по комбинациям измерений, по каждому измерению и по всей выборке, запрос становится длиннее и сложнее для сопровождения. При ручном объединении нужно также корректно различать детальные строки и строки итогов.

Неверный выбор механизма может привести к лишним уровням агрегации или, наоборот, к отсутствию нужных итогов. Кроме того, итоговые строки обычно содержат NULL в колонках измерений, поэтому их нельзя безоговорочно считать обычными группами.

Подробное решение

CUBE(a, b) формирует четыре уровня группировки: (a, b), (a), (b) и (). Последняя запись соответствует общему итогу. Для трёх измерений будет создано восемь комбинаций, то есть 2^3 уровней группировки, если данные позволяют получить строки на каждом уровне.

Минимальный пример:

WITH sales(region, year, amount) AS ( VALUES ('Север', 2024, 100), ('Юг', 2024, 150), ('Север', 2025, 120) ) SELECT region, year, SUM(amount) AS total_amount FROM sales GROUP BY CUBE (region, year);

В результате будут детальные итоги по региону и году, итоги по каждому региону за все годы, итоги по каждому году за все регионы и общий итог. Порядок строк результата не следует считать гарантированным без отдельного ORDER BY.

CUBE отличается от ROLLUP тем, что ROLLUP создаёт иерархические префиксные итоги: например, (region, year), (region) и (). Он не создаёт отдельный итог только по year, тогда как CUBE создаёт все комбинации.

Если требуются не все комбинации, а строго выбранный набор уровней, точнее использовать GROUPING SETS. Он обычно лучше контролирует объём результата, поскольку CUBE может порождать много строк при большом числе измерений.

NULL в колонке измерения может означать как настоящее значение NULL, так и отсутствие измерения в строке итога. Для различения этих случаев применяют функцию GROUPING, которая показывает, была ли колонка исключена из конкретного набора группировки.

Ситуация из практики

В финансовом отчёте нужно показать продажи по парам «регион — год», итоги по регионам, итоги по годам и общий итог. Вариант с несколькими агрегатными запросами и UNION ALL прозрачен, но требует повторять формулу продаж и согласовывать типы и порядок столбцов.

ROLLUP не подходит: он даст итоги по регионам и общий итог, но не сформирует итоговые строки только по годам. GROUPING SETS решит задачу явно, однако список уровней придётся поддерживать вручную.

Выбором становится CUBE, потому что требуются ровно все комбинации двух измерений. Результат строится одной агрегатной логикой, а дополнительное поле с признаком уровня группировки позволяет корректно отображать строки в отчёте.

Что кандидаты часто упускают

  1. Чем CUBE отличается от перечисления всех наборов в GROUPING SETS?

    По результату при полном перечислении наборов они эквивалентны. Разница в способе выражения требования: CUBE компактно означает «все комбинации», а GROUPING SETS позволяет выбрать только нужные комбинации. Поэтому при большом числе измерений GROUPING SETS может быть предпочтительнее из-за меньшего результата и более явного контроля.

  2. Почему количество строк после CUBE не обязательно равно сумме количества строк на всех уровнях?

    Каждая комбинация группировки агрегирует фактические сочетания значений, а не декартово произведение всех возможных значений измерений. Если для некоторого сочетания в исходных данных нет строк, отдельная группа обычно не появляется. Поэтому число строк зависит от данных и может быть меньше теоретического максимума.

  3. Почему итоговую строку нельзя надёжно определить проверкой на NULL в измерении?

    Реальная исходная группа тоже может иметь NULL в измерении. Поэтому проверка вида «если регион равен NULL, значит это общий итог» смешивает две разные ситуации. Для надёжного различения нужно использовать GROUPING или эквивалентный признак уровня группировки, предоставляемый конкретной СУБД.