Разберите SQL-кейс: почему запрос SELECT * FROM orders WHERE order_date > '2020-01-01' не использует индекс по order_date и как это исправить на уровне схемы и запроса?

17 Ноя в 06:52
2 +1
0
Ответы
1
Коротко — причины и способы исправления.
Почему индекс по order_date\text{order\_date}order_date может не использоваться
- На колонке применяется функция/выражение, например DATE(order_date)>′2020−01−01′\text{DATE(order\_date)} > '2020-01-01'DATE(order_date)>20200101. Индексы на исходную колонку тогда не работают.
- Типы не совпадают / происходит приведение (implicit cast), что может помешать использованию индекса.
- Индекс отсутствует или он составной, где order_date\text{order\_date}order_date не стоит в левом (leading) положении — тогда индекс не годится для данного предиката.
- Низкая селективность: если большая часть строк удовлетворяет order_date>′2020−01−01′\text{order\_date} > '2020-01-01'order_date>20200101, оптимизатор предпочитает полный скан.
- Отсутствие покрытия: вы пишете SELECT *\text{SELECT *}SELECT *, чтение по вторичному индексу приведёт к множественным random I/O, и оптимизатор может выбрать full table scan.
- Статистика устарела — оптимизатор неверно оценивает стоимость.
Как исправить на уровне схемы
- Добавить простой индекс:
CREATE INDEX idx_orders_order_date ON orders(order_date);
- Если часто выбираете только несколько полей, сделать покрывающий индекс (DB-specific):
- MySQL/Postgres: CREATE INDEX idx ON orders(order_date, other_col);
- SQL Server: CREATE NONCLUSTERED INDEX idx ON orders(order_date) INCLUDE (other_col1, other_col2);
- Если запрос использует функцию над датой, создать expression/functional индекс или генерируемую колонку и индексировать её:
- Postgres: CREATE INDEX idx_date_trunc ON orders ((date(order_date)));
- MySQL (если нет expression index): ALTER TABLE orders ADD COLUMN order_date_only DATE GENERATED ALWAYS AS (DATE(order_date)) STORED; CREATE INDEX idx_order_date_only ON orders(order_date_only);
- Убедиться, что тип колонки — DATE/DATETIME/TIMESTAMP, а не текстовый, чтобы избежать неявных преобразований.
- Обновить статистику: ANALYZE / VACUUM ANALYZE / UPDATE STATISTICS.
Как исправить на уровне запроса
- Не оборачивать колонку в функции; писать прямо: order_date>′2020−01−01′\text{order\_date} > '2020-01-01'order_date>20200101 (или явно привести литерал к типу даты: order_date>DATE′2020−01−01′\text{order\_date} > \text{DATE} '2020-01-01'order_date>DATE20200101 в Postgres).
- Если нужно сравнивать по дате без времени, используйте либо границы (например order_date≥′2020−01−01′∧order_date<′2020−01−02′\text{order\_date} \ge '2020-01-01' \land \text{order\_date} < '2020-01-02'order_date20200101order_date<20200102), либо индексированную генерируемую колонку/функциональный индекс.
- Ограничить вывод колонок, чтобы можно было использовать covering index вместо SELECT *\text{SELECT *}SELECT *.
- При сомнениях — посмотреть план: EXPLAIN SELECT ... и при необходимости проанализировать/обновить статистику.
Примеры
- Создать индекс: CREATE INDEX idx_orders_order_date ON orders(order_date);
- Переписать запрос (не оборачивая колонку): SELECT * FROM orders WHERE order_date>′2020−01−01′\text{order\_date} > '2020-01-01'order_date>20200101.
- Если раньше был WHERE DATE(order_date) > ’2020-01-01’\text{WHERE DATE(order\_date) > '2020-01-01'}WHERE DATE(order_date) > ’2020-01-01’, сделать генерируемую колонку + индекс и писать по ней.
Рекомендация: сначала проверить EXPLAIN, затем добавить/перестроить индекс и обновить статистику; если селективность всё ещё низкая — индекс может и не помочь.
17 Ноя в 06:59
Не можешь разобраться в этой теме?
Обратись за помощью к экспертам
Гарантированные бесплатные доработки в течение 1 года
Быстрое выполнение от 2 часов
Проверка работы на плагиат
Поможем написать учебную работу
Прямой эфир