HAVING
О HAVING и немного оптимизации
Вернёмся к нашей таблице data_analyzer.diagnosis_dst_meta из предыдущего примера. Добавим группировку по ключу партиционирования и отфильтруем данные двумя способами:
SELECT toYYYYMM(signed_dt), count() as cnt
FROM data_analyzer.diagnosis_dst_meta
GROUP BY toYYYYMM(signed_dt)
HAVING toYYYYMM(signed_dt) IN (202501, 202502, 202503)
SELECT toYYYYMM(signed_dt), count() as cnt FROM data_analyzer.diagnosis_dst_meta
WHERE toYYYYMM(signed_dt) IN (202501, 202502, 202503)
GROUP BY toYYYYMM(signed_dt)
По логике в первом запросе сначала читается вся таблица, затем происходит группировка, затем фильтрация по агрегированному полю. Во втором запросе читаются только нужные партиции и кажется, что он существенно оптимальнее по производительности. Проверим:

Мы видим, что результаты практически идентичны (только незначительное различие по памяти). Это произошло, потому что на самом деле первый запрос не был выполнен, как мы его задумали, а был оптимизирован кликхаусом (тут мы обсуждаем эту оптимизацию). Давайте в этом убедимся при помощи оператора EXPLAIN SYNTAX — эта конструкция показывает, как выглядит запрос после внутренней оптимизации:
EXPLAIN SYNTAX SELECT toYYYYMM(signed_dt), count() as cnt
FROM data_analyzer.diagnosis_dst_meta
GROUP BY toYYYYMM(signed_dt)
HAVING toYYYYMM(signed_dt) IN (202501, 202502, 202503)
EXPLAIN SYNTAX SELECT toYYYYMM(signed_dt), count() as cnt
FROM data_analyzer.diagnosis_dst_meta
WHERE toYYYYMM(signed_dt) IN (202501, 202502, 202503)
GROUP BY toYYYYMM(signed_dt)

Несмотря на то, что кликхаус исправляет такие ошибки пользователя, происходит это не всегда и спрогнозировать работу оптимизатора невозможно. Поэтому правильно использовать WHERE вместо HAVING если это возможно
Выводы
Не используйте HAVING там, где можно обойтись WHERE
Используйте EXPLAIN SYNTAX, чтобы увидеть, как запрос выглядит после обработки оптимизатором
Last updated