Learning Platform
Глоссарий Troubleshooting
Урок 09.03 · 25 мин
Продвинутый
WITH ROLLUPWITH CUBEWITH TOTALSGROUPINGGROUP BY

WITH ROLLUP, CUBE и TOTALS

Стандартный GROUP BY выдаёт строки только по конкретным комбинациям ключей. Но аналитические отчёты часто требуют подитоги: “продажи по странам и городам, плюс итог по стране, плюс общий итог”. Без модификаторов — это 3 отдельных запроса с UNION ALL.

ClickHouse поддерживает 3 модификатора GROUP BY:

  • WITH ROLLUP — иерархические подитоги (справа налево)
  • WITH CUBE — все возможные комбинации (2^n)
  • WITH TOTALS — одна строка общего итога

WITH ROLLUP: иерархические подитоги

WITH ROLLUP: иерархия подитогов (справа налево)
country + city (детальные строки)Уровень 0 (самый детальный): GROUP BY country, city. Каждая комбинация страна+город -- отдельная строка. Например: Russia/Moscow, Russia/SPb, Germany/Berlin.
country (подитог по стране)Уровень 1 (подитог): GROUP BY country. Столбец city заменён на default-значение (пустая строка). Итог по каждой стране: Russia/total, Germany/total.
Общий итог (все столбцы = default)Уровень 2 (общий итог): все столбцы GROUP BY заменены на default. Одна строка с общим итогом по всем данным. country='' и city=''.

ROLLUP удаляет столбцы группировки справа налево, создавая подитоги на каждом уровне:

SELECT country, city, sum(revenue) AS total
FROM sales
GROUP BY country, city WITH ROLLUP
ORDER BY country, city;
countrycitytotal
GermanyBerlin5000
GermanyMunich3000
Germany8000
RussiaMoscow7000
RussiaSPb4000
Russia11000
19000

Для GROUP BY a, b, c WITH ROLLUP создаются уровни:

  • (a, b, c) — детальные строки
  • (a, b) — подитог без c
  • (a) — подитог без b, c
  • () — общий итог

Итого: n + 1 уровень для n столбцов.


WITH CUBE: все комбинации

CUBE создаёт подитоги для всех возможных комбинаций столбцов GROUP BY:

SELECT year, quarter, sum(revenue) AS total
FROM sales
GROUP BY year, quarter WITH CUBE;

Для GROUP BY a, b WITH CUBE создаются комбинации:

  • (a, b) — детальные строки
  • (a) — подитог без b
  • (b) — подитог без a
  • () — общий итог

Для n столбцов: 2^n комбинаций.

yearquartertotal
2025Q110000
2025Q212000
2026Q115000
202522000
202615000
Q125000
Q212000
37000
TIP

ROLLUP подходит для иерархий (страна, город, район). CUBE — для перекрёстных отчётов (год, квартал, регион), где важны подитоги по каждому измерению независимо.


WITH TOTALS: одна строка итога

WITH TOTALS добавляет одну дополнительную строку с общим итогом. В отличие от ROLLUP и CUBE, не создаёт промежуточных подитогов:

SELECT country, sum(revenue) AS total
FROM sales
GROUP BY country WITH TOTALS;
countrytotal
Germany8000
Russia11000
19000

WITH TOTALS особенно полезен с HAVING: итог вычисляется до фильтрации HAVING, показывая полную картину:

SELECT country, sum(revenue) AS total
FROM sales
GROUP BY country WITH TOTALS
HAVING total > 10000;
countrytotal
Russia11000
19000

GROUPING(): дизамбигуация подитогов

Главная проблема ROLLUP и CUBE: подитоги имеют default-значения (пустая строка, 0) в агрегированных столбцах. Как отличить подитог от реальной строки с пустым значением?

-- Проблема: country='' -- это подитог или реальная страна с пустым названием?
SELECT country, city, sum(revenue) AS total
FROM sales
GROUP BY country, city WITH ROLLUP;

Функция GROUPING() решает эту проблему. Она возвращает битовую маску, показывающую какие столбцы были агрегированы:

SELECT
    country,
    city,
    sum(revenue) AS total,
    GROUPING(country) AS g_country,
    GROUPING(city) AS g_city
FROM sales
GROUP BY country, city WITH ROLLUP
ORDER BY g_country, g_city, country, city;
countrycitytotalg_countryg_city
GermanyBerlin500000
GermanyMunich300000
RussiaMoscow700000
RussiaSPb400000
Germany800001
Russia1100001
1900011
  • GROUPING(col) = 0 — столбец участвует в группировке (реальное значение)
  • GROUPING(col) = 1 — столбец агрегирован (значение — подитог)
-- Множественный GROUPING() с битовой маской:
SELECT
    country, city,
    sum(revenue) AS total,
    GROUPING(country, city) AS grp
FROM sales
GROUP BY country, city WITH ROLLUP;
-- grp=0: обычная строка (country + city)
-- grp=1: подитог по country (city агрегирован)
-- grp=3: общий итог (оба агрегированы)
WARNING

Без GROUPING() подитоги неотличимы от строк с реальными пустыми значениями. Всегда используйте GROUPING() если данные могут содержать пустые строки или нули в столбцах GROUP BY.


Практический пример: отчёт о продажах

SELECT
    CASE WHEN GROUPING(region) = 1 THEN 'ВСЕ РЕГИОНЫ' ELSE region END AS region,
    CASE WHEN GROUPING(category) = 1 THEN 'ВСЕ КАТЕГОРИИ' ELSE category END AS category,
    sum(revenue) AS revenue,
    count() AS orders,
    avg(revenue) AS avg_order
FROM sales
WHERE sale_date >= '2025-01-01'
GROUP BY region, category WITH CUBE
ORDER BY GROUPING(region, category), region, category;

Результат — полная перекрёстная таблица с читаемыми подписями подитогов вместо пустых строк.


Сравнение модификаторов

МодификаторДополнительные строкиСценарий
WITH ROLLUPn + 1 уровень (иерархия)Иерархические отчёты: страна, город, район
WITH CUBE2^n комбинацийПерекрёстные отчёты: год, квартал, категория
WITH TOTALS1 строка (общий итог)Простой итог, особенно полезен с HAVING

Ключевые выводы

  1. WITH ROLLUP — иерархические подитоги, столбцы удаляются справа налево. Идеален для drill-down отчётов.
  2. WITH CUBE — все 2^n комбинаций. Для перекрёстных отчётов с независимыми измерениями.
  3. WITH TOTALS — одна строка общего итога. Показывает total до HAVING-фильтрации.
  4. GROUPING() — обязательна для дизамбигуации: отличает подитоги от реальных пустых значений.
  5. Без GROUPING() нельзя надёжно отличить подитог от строки с country = ''.
PostgreSQL: ROLLUP, CUBE, GROUPING SETS и GROUPING() Многомерный анализ: OLAP cube, slice, drill-down

Проверьте понимание

Результат: 0 из 0
Прикладной
Вопрос 1 из 4. Отчёт о продажах должен показывать выручку по иерархии: страна, город, с подитогами по странам и общим итогом. Какой модификатор GROUP BY подходит?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 10