Очень частая ситуация: индекс создан, запрос написан вроде нормально, но SQL всё равно делает Seq Scan или Full Table Scan.
Имеем такую таблицу:
users(
id,
email,
created_at
)
Индекс:
CREATE INDEX idx_users_email
ON users(email);
Кажется, что такой запрос точно должен использовать индекс:
SELECT *
FROM users
WHERE email = 'test@example.com';
И обычно действительно будет Index Scan.
Но достаточно небольшой детали — и индекс может перестать использоваться. Например:
SELECT *
FROM users
WHERE LOWER(email) = 'test@example.com';
Проблема в том, что индекс построен по колонке:
email. А в условии используется выражение:LOWER(email)
Для оптимизатора это уже не то же самое условие. В итоге серверу часто приходится: применять
LOWER() к строкам; сравнивать результат; читать гораздо больше данных, чем ожидалось.Исправляется это
expression/functional index:CREATE INDEX idx_users_email_lower
ON users(LOWER(email));
То же самое часто происходит с датами:
SELECT *
FROM orders
WHERE DATE(created_at) = '2025-01-10';
Из-за:
DATE(created_at)
обычный индекс по
created_at может не помочь, потому что функция применяется к колонке.Правильнее писать диапазон:
SELECT *
FROM orders
WHERE created_at >= '2025-01-10'
AND created_at < '2025-01-11';
Так условие остаётся sargable — то есть пригодным для эффективного использования индекса.
Ещё одна частая проблема — преобразования типов. Например, плохо:
WHERE user_id::text = '100'
Если
user_id — integer, то здесь преобразование применяется к колонке. В такой ситуации обычный индекс по user_id может не использоваться.Правильнее:
WHERE user_id = 100
Важно: простое условие вида:
WHERE user_id = '100'
в некоторых СУБД может нормально привести литерал к integer и всё равно использовать индекс. Проблема чаще начинается там, где преобразуется сама колонка или выражение становится сложнее.
Популярная ошибка с
LIKE:WHERE email LIKE '%gmail.com'
Здесь обычный B-Tree индекс обычно бесполезен. Так как поиск начинается не с начала строки; сервер не может эффективно использовать упорядоченность индекса.
Если таблица маленькая:
100–1000 строк, то Full Scan может быть дешевле, чем прыжки по индексу.🔥 Наличие индекса ещё не гарантирует его использование. Функции над колонками, преобразования типов, неправильные
LIKE, OR-условия и устаревшая статистика могут сделать индекс бесполезным или менее выгодным для оптимизатора.➡️ SQL Ready | #практика