Заставьте WHERE DATE(created_at) = '2026-01-01' использовать индекс по created_at
Отчёт фильтрует один день, но запрос делает полный Seq Scan, хотя по created_at есть индекс. Предикат оборачивает столбец в функцию.
Ограничение: вернуть те же строки (весь 2026-01-01), но дать использовать B-tree индекс по created_at. Считайте, что created_at имеет тип timestamp.
SELECT id, user_id, amount
FROM orders
WHERE DATE(created_at) = '2026-01-01';
Перепишите предикат так, чтобы он стал sargable.
Обёртка столбца в DATE() делает предикат не-sargable — функция вычисляется на каждой строке, поэтому индекс по created_at бесполезен и планировщик делает Seq Scan. Перепишите его полуоткрытым диапазоном по «голому» created_at >= '2026-01-01' AND created_at < '2026-01-02'.
- ✗Приводить константу вместо снятия функции со столбца
- ✗Винить список SELECT, а не не-sargable предикат
- ✗Форсировать enable_seqscan off вместо того, чтобы сделать предикат sargable
- →Когда индекс по выражению DATE(created_at) — лучший фикс?
- →Почему полуоткрытый диапазон надёжнее приведения к date для timestamp?
Функция DATE(created_at) вычисляется для каждой строки, поэтому планировщик не может свести предикат к диапазону по индексу и делает Seq Scan. Уберите функцию со столбца — задайте полуоткрытый диапазон по «голому» created_at:
SELECT id, user_id, amount
FROM orders
WHERE created_at >= '2026-01-01'
AND created_at < '2026-01-02';
Теперь предикат sargable: B-tree по created_at даёт Index/Range Scan только по нужному дню. Полуоткрытый диапазон (< следующего дня) корректно охватывает timestamp с любым временем суток, в отличие от равенства = '2026-01-01'. Альтернатива — индекс по выражению CREATE INDEX ON orders (DATE(created_at)), но диапазон по столбцу проще и не требует лишнего индекса.