Если в ClickHouse вы лечите задачи подзапросами и джойнами “на всякий случай” — вы теряете часы на ровном месте.

Сценарий знакомый: нужно быстро ответить “что изменилось”, “где провал”, “какие сегменты тащат метрику”. А запрос расползается: фильтры в нескольких местах, ручные CASE, отдельная таблица для “только успешных”, потом ещё один слой агрегации. Цена ошибки тут не только во времени: такие конструкции легко ломаются при правке, а результат сложно проверить глазами.

Механика простая: ClickHouse хорошо умеет считать в один проход, но мы часто заставляем его делать то, что можно выразить функцией прямо внутри агрегации или над потоком строк. В итоге появляется лишняя сложность, больше мест для расхождений и больше поводов “давай пересоберём витрину”.

Минимальный набор “вспомнить и применить”, когда надо ускориться без героизма:

  1. Условные агрегаты: sumIf, countIf, avgIf, uniqIf. Кейс: воронка или качество трафика в одном запросе — заказы, отмены, возвраты, “успешные” и “все” рядом, без CASE-поля и без отдельной предагрегации.
  2. Приблизительные оценки: uniqCombined64, quantileTDigest (и семейство). Кейс: “сколько уникальных” и “какая медиана/95p” по сотням разрезов для мониторинга. Быстро для поиска проблем, а точный пересчёт — только для финального ответа.
  3. Массивы и кортежи: groupArray, arrayFilter, arrayMap, arrayJoin, tuple. Кейс: собрать “топ причин” или “последние N событий пользователя” и разложить их уже на уровне запроса, без отдельной ETL-логики и без ручной склейки строк.
  4. Оконные функции: row_number, lagInFrame, leadInFrame, sum/count over. Кейс: retention по когортам, “предыдущее значение метрики”, поиск разрывов в событиях, дедупликация по “берём последнюю запись” без самоджойна таблицы на себя.
  5. JSON и Map: JSONExtract*, mapGet, mapKeys/mapValues. Кейс: события с динамическими параметрами — быстро вытащить нужные атрибуты, посчитать метрику по ключу, сделать срез по версии/эксперименту/платформе без распила схемы под каждое новое поле.

В хорошей практике это выглядит так: сначала формулируете метрику и разрезы, потом пытаетесь выразить расчёт “в один проход” через if-агрегации, окна и работу со структурами, и только затем добавляете джойны как последнюю меру. Ограничение простое: приблизительные функции и JSON-парсинг в тяжёлых витринах требуют дисциплины — для отчётности и денег лучше иметь точный слой или чётко оговорённую погрешность. Обычно спорят именно об этом, а не о синтаксисе.