WHERE

Фильтрация

В этой главе мы рассмотрим разные примеры фильтрации и сравним их между собой.

Фильтрация по ключу сортировки

Посмотрим последние строки DDL таблицы, которую мы будем рассматривать. Здесь — подробнее о DDL.

... # код создания таблицы
PRIMARY KEY patient_id -- индекс
ORDER BY patient_id -- ключ сортировки
SETTINGS index_granularity = 8192;

Посмотрим, насколько меняется потребление ресурсов при фильтрации. Есть такой запрос, где происходит фильтрация по полю patient_gender:

SELECT patient_id FROM m_students.v_students_light_1;
SELECT patient_id FROM m_students.v_students_light_1 WHERE patient_gender = 'ж';
Потребление ресурсов

При фильтрации по полю patient_gender происходит чтение дополнительной колонки, отсюда увеличивается потребление памяти. Количество прочитанных строк в обоих случаях одинаково — это вся таблица. Затраты оперативной памяти отличаются незначительно. Поле patient_gender не является ключом сортировки и при фильтрации по нему читается вся таблица.

Посмотрим следующий пример фильтрации — в первом случае мы фильтруем по полю dn_id (не является ключом сортировки), а во втором случае — по полю patient_id (т.е. по ключу сортировки):

SELECT dn_id FROM m_students.v_students_light_1 WHERE dn_id IN (<list>)
SELECT patient_id FROM m_students.v_students_light_1 WHERE patient_id IN (<list>)

Списки dn_id и patient_id — это случайно выбранные 10 id из всей таблицы.

Снижение потребления ресурсов при фильтрации по ключу сортировки

Мы видим насколько снижено потребление ресурсов при фильтрации по ключу сортировки — patient_id. Обратите внимание, что в таком случае не требуется прочтение всей таблицы.

Фильтрация по ключу партиционирования

Если таблица партиционирована (подробнее), то нужно фильтровать в первую очередь по ключу партиционирования, а затем по ключу сортировки.

Для примера возьмем партиционированную таблицу data_analyzer.diagnosis_dst_meta. Всего в таблице примерно 1.36 миллиарда строк. Заглянем в DDL таблицы:

... # код создания таблицы
PARTITION BY toYYYYMM(signed_dt)
PRIMARY KEY (document_class_id,
 document_id)
ORDER BY (document_class_id,
 document_id,
 xpath,
 line_id)
SETTINGS index_granularity = 8192;

Сначала попробуем отфильтровать данные НЕ по ключу партиционирования: по колонке document_created:

SELECT document_id FROM data_analyzer.diagnosis_dst_meta
WHERE document_created > '2025-03-10' # колонка с датой
AND type = 'основной диагноз' # просто пример фильтрации по обычному полю

Потребление ресурсов значительное, потому что читается вся таблица (см. количество прочитанных строк).

Теперь попробуем фильтровать по ключу партиционирования:

SELECT document_id FROM data_analyzer.diagnosis_dst_meta
WHERE signed_dt > '2025-03-10' # ключ партиционирования
AND type = 'основной диагноз'

Обратите внимание, читается гораздо меньше строк, потому что кликхаус сразу идёт только в нужные партиции, а остальные ему читать не нужно. Потребление оперативной памяти — примерно в 35 раз меньше.

Проведём последний эксперимент в этой секции, попробуем профильтровать те же партиции, но будем использовать фильтрацию по ключу сортировки document_class_id вместо фильтрации по полю type:

SELECT document_id FROM data_analyzer.diagnosis_dst_meta
WHERE signed_dt > '2025-03-10' # ключ партиционирования
AND document_class_id IN (<список из class_id>) # ключ сортировки

Обратите внимание, что несмотря на то, что количество строк в результате запроса больше (2.2 млн против 1.93 млн) мы потратили меньше ресурсов и прочли меньше строк.

Порядок в секции WHERE важен — используйте сначала ключ партиционирования, затем ключ сортировки, затем другие фильтрации. Хотя ClickHouse оптимизирует фильтрацию даже если вы нарушите порядок, хорошим тоном является соблюдать такой порядок фильтрации в запросе.

Выводы

  • Используйте фильтрацию по ключу партиционирования и ключам сортировки, если это возможно

  • Порядок фильтрации в WHERE следующий: ключ партиционирования, ключ сортировки, остальные поля

Last updated