Агрегатные комбинаторы: -If, -Array, -State/-Merge

Один суффикс превращает обычную агрегатную функцию в новый инструмент.

Комбинатор — суффикс, добавляемый к имени агрегатной функции (например, sumIf, uniqState), который меняет её поведение.

Идея комбинаторов

Вместо десятков отдельных функций ClickHouse даёт «конструктор»: берёте любую агрегатную функцию и приклеиваете суффикс. Так из sum получается sumIf, sumArray, sumState. Разберём главные.

-If: условная агрегация

Суффикс -If добавляет функции дополнительный аргумент-условие — агрегируются только строки, где оно истинно. Это позволяет посчитать несколько срезов за один проход по данным:

SELECT
    count()                       AS all_events,
    countIf(country = 'RU')       AS ru_events,
    sumIf(amount, status = 'paid') AS paid_revenue,
    avgIf(duration, success = 1)  AS avg_ok_duration
FROM events;

Без -If пришлось бы делать несколько отдельных запросов или громоздкие CASE.

-Array: агрегация по массивам

Суффикс -Array применяет функцию к элементам массивов в колонке, как будто они «развёрнуты». Если в колонке tags лежат массивы строк, uniqArray(tags) посчитает уникальные теги по всем массивам сразу.

-State и -Merge: самое мощное

Это пара для предагрегации. Обычная функция возвращает готовое число. Функция с суффиксом -State возвращает промежуточное состояние агрегата — компактный «полуфабрикат» (например, скетч для uniq или пару «сумма+счётчик» для avg), который можно сохранить и потом доагрегировать.

-- Сохраняем состояния (не финальные числа), например в таблицу с AggregatingMergeTree:
SELECT
    day,
    uniqState(user_id)   AS users_state,
    avgState(duration)   AS dur_state
FROM events
GROUP BY day;

Позже из этих состояний получаем финальный результат функцией с суффиксом -Merge, причём можем дополнительно сворачивать (например, дни в недели):

SELECT
    toStartOfWeek(day)        AS week,
    uniqMerge(users_state)    AS weekly_users,
    avgMerge(dur_state)       AS weekly_avg_dur
FROM daily_states
GROUP BY week;

Почему -State/-Merge так важны

Состояния складываемы: состояние за день + состояние за день = состояние за два дня. Поэтому можно один раз свернуть сырые события в посуточные состояния (мало строк), а потом мгновенно собирать из них отчёты за любой период — не трогая исходные миллиарды строк. На этом построены материализованные представления (следующий раздел).

Памятка

СуффиксЧто делает
-Ifагрегирует только по условию
-Arrayагрегирует по элементам массивов
-Stateвозвращает промежуточное состояние
-Mergeсворачивает состояния в финал

Как работает под капотом

Состояние агрегата — это сериализованная внутренняя структура функции (для avg — сумма и количество, для uniq — HyperLogLog-скетч). Тип такого столбца — AggregateFunction(...). -Merge объединяет несколько состояний так же, как это делается при параллельном вычислении на разных ядрах.

Частые ошибки

  • Складывать готовые uniq-числа. Сумма уникальных по дням ≠ уникальные за неделю (один и тот же пользователь посчитается дважды). Складывать нужно состояния через -State/-Merge.
  • Читать состояние как обычное число. Колонку AggregateFunction нужно «финализировать» через -Merge, иначе получите нечитаемый бинарный объект.
  • Громоздкие CASE вместо -If. countIf/sumIf чище и быстрее для срезов.

Итоги

  • Комбинаторы — суффиксы, расширяющие агрегатные функции.
  • -If — условные срезы за один проход; -Array — работа с массивами.
  • -State/-Merge — складываемые промежуточные состояния для предагрегации.
  • Уникальные нельзя суммировать как числа — только через состояния.
Проверьте себя
1. Что делает суффикс -If, например в countIf(country = 'RU')?
AСчитает строки только там, где условие истинно
BУдаляет строки с условием
CДелает функцию приближённой
DСортирует результат
2. Почему для подсчёта недельных уникальных пользователей нужны -State/-Merge, а не сумма дневных uniq?
AТак быстрее печатать
BОдин пользователь активен в разные дни, и сумма дневных uniq его задвоит; складывать надо состояния
Cuniq не работает по дням
DСумма всегда даёт ноль
3. Что возвращает функция с суффиксом -State, например uniqState(user_id)?
AГотовое число уникальных
BПромежуточное складываемое состояние агрегата
CСписок всех user_id
DОшибку