Когда использовать составные индексы?
Что проверяют
Понимаете ли вы, что индекс проектируется под конкретные запросы, а не «навешивается на важные колонки». Вопрос быстро выводит на порядок колонок и цену записи. Следом обычно спрашивают, почему индекс по (a, b) не помогает запросу по одному b, и как вы проверяете, что индекс вообще используется.
Как отвечать
Начните с правила левого префикса: индекс по (a, b, c) работает для условий по a, по a и b, по всем трём, но бесполезен для запроса только по b. Отсюда и порядок колонок:
- порядок: сначала колонки под равенство, затем колонка под диапазон, затем то, по чему идёт сортировка. Совет «самая селективная первой» это миф, порядок диктует набор запросов;
- сортировка: индекс закрывает
ORDER BYтолько при совпадении порядка и направления, для смешанныхASCиDESCнужен индекс с теми же направлениями; - покрывающий индекс:
INCLUDEв SQL Server и PostgreSQL кладёт дополнительные колонки в лист, запрос обходится без обращения к таблице, а из плана исчезает Key Lookup; - фильтрованный или частичный индекс: условие
WHERE is_active = 1резко уменьшает размер, когда запросы идут с этим фильтром; - уникальность: составной уникальный индекс запрещает повтор комбинации значений.
Назовите цену: каждый индекс замедляет запись, занимает место и попадает в бэкапы, а в SQL Server ключ кластерного индекса дописывается в каждый некластерный. Индекс по (a, b) при существующем (a, b, c) избыточен. В EF Core индекс задаётся HasIndex(e => new { e.CompanyId, e.CreatedAt }) с IncludeProperties и HasFilter, но проверяется всё равно планом: SET STATISTICS IO ON и фактический план в SQL Server, EXPLAIN (ANALYZE, BUFFERS) в PostgreSQL.
Где спотыкаются
Создают по индексу на каждую колонку и надеются, что база их объединит. Пересечение индексов встречается редко и проигрывает одному составному.
Оборачивают колонку функцией или сравнивают с другим типом: WHERE YEAR(created_at) = 2026 убивает поиск по индексу, нужен диапазон дат или вычисляемый индекс.
Добавляют индексы по подсказкам оптимизатора и никогда не удаляют старые. Через год таблица тащит десяток похожих индексов, а запись просела втрое.