Как оптимизировать порядок условий в запросе с несколькими колонками
Что проверяют
Знаете ли вы, что порядок условий в WHERE на план почти не влияет, а важен порядок колонок в составном индексе. Интервьюер проверяет, не повторяете ли вы миф про «самое селективное условие ставим первым». Следом просят спроектировать индекс под конкретный запрос и прочитать EXPLAIN.
Как отвечать
Начните с главного: оптимизатор в PostgreSQL, MySQL и Oracle стоимостный. Он сам решает, какой индекс взять и в каком порядке проверять условия, поэтому a = 1 AND b = 2 и b = 2 AND a = 1 дают одинаковый план. Короткое замыкание, как в Java, SQL не гарантирует.
Порядок имеет значение в составном индексе:
- левый префикс: индекс
(customer_id, status)работает для условия поcustomer_idи по паре, но не по одномуstatus. В PostgreSQL 18 появился skip scan для B-tree, но проектировать индекс в расчёте на него не стоит; - равенство раньше диапазона: для
customer_id = ? AND created_at > ?подходит(customer_id, created_at). Колонки после диапазона уже не сужают поиск по индексу; - сортировка:
(customer_id, status, order_date)закрывает и фильтр, иORDER BY order_date DESCбез отдельной сортировки; - селективность: первой ставят колонку, по которой запрос фильтрует всегда, а среди равноправных более селективную. Статус с тремя значениями редко годится на первое место.
Со стороны Java индекс объявляют в миграции Flyway или Liquibase, а запрос, который генерирует Hibernate или Spring Data по имени метода, смотрят в логе SQL. Проверка всегда одна: EXPLAIN (ANALYZE, BUFFERS) на реальном объёме данных.
Где спотыкаются
Переставляют условия в WHERE и видят ускорение, которое на деле дал прогретый кеш. Без сравнения планов такой вывод ничего не стоит.
Оборачивают колонку в функцию или приводят тип: WHERE DATE(created_at) = ? не использует индекс по created_at, нужен диапазон.
Объединяют условия по разным колонкам через OR. Один составной индекс такой запрос не покрывает, помогают отдельные индексы с bitmap-объединением или UNION.