Ответ
Оптимизатор СУБД может выбрать полное сканирование таблицы (FULL TABLE SCAN) вместо использования индекса в следующих основных случаях:
-
Низкая селективность условия: Если условие
WHEREохватывает большую часть таблицы (например, >15-20% строк). Сканирование по индексу с последующими обращениями к таблице (TABLE ACCESS BY INDEX ROWID) становится дороже, чем прямое последовательное чтение.-- Индекс по is_active вряд ли будет использован, если 90% строк active SELECT * FROM users WHERE is_active = TRUE; -
Отсутствие подходящего префикса индекса: Для составного индекса
(A, B)запрос с фильтром только поBне сможет его использовать эффективно. -
Использование функций или модификаторов столбца: Индекс по
created_atне будет использован, если применить к нему функцию.-- Не использует индекс по created_at SELECT * FROM logs WHERE DATE(created_at) = '2023-10-01'; -- Решение: создать функциональный индекс или переписать запрос SELECT * FROM logs WHERE created_at >= '2023-10-01' AND created_at < '2023-10-02'; -
Неактуальная статистика: Если статистика по таблице устарела, оптимизатор может ошибиться в оценке стоимости.
-
Подсказка оптимизатору или маленькая таблица: Для очень маленьких таблиц сканирование всегда дешевле. Также можно явно указать
/*+ FULL(table_name) */.
Как проверить? Используйте EXPLAIN (или EXPLAIN ANALYZE):
EXPLAIN ANALYZE
SELECT * FROM large_table WHERE low_selectivity_column = 'common_value';
-- В плане выполнения будет видно "Seq Scan" вместо "Index Scan" Ответ 18+ 🔞
Да ты посмотри, что творится! Оптимизатор этой вашей СУБД — он же не дурак, он, блядь, считает. И если ему кажется, что просканировать всю таблицу целиком будет быстрее, чем прыгать по индексу, он так и сделает. Сам от себя охуеешь, когда ждёшь быстрого индекса, а он тебе в ответ — полное сканирование, ёпта.
Вот основные причины, почему эта мартышлюшка так поступает:
-
Условие в
WHEREслишком широкое. Если ты выбираешь, условно, 90% строк из таблицы, то какой смысл лазить по индексу, а потом ещё по таблице бегать за каждой строчкой? Дешевле сразу всё прочитать пачкой. Это как вместо того, чтобы по номеру квартиры искать человека в доме, зайти и крикнуть: «Эй, все выходите!» — если выходить должны почти все.-- Индекс по is_active тут просто накрылся медным тазом, если почти все пользователи активны. SELECT * FROM users WHERE is_active = TRUE; -
Ты лезешь не с того конца. Есть у тебя составной индекс
(A, B). А ты в запросе фильтруешь только поB. Ну и как он его использует, этот индекс? Никак. Это всё равно что искать книгу в библиотеке только по последней странице. Хуй с горы, а не поиск. -
Ты изгаляешься над столбцом. Навесил на него функцию в условии — и всё, приехали. Индекс по
created_atлежит мёртвым грузом.-- Вот так делать — пиздопроебибна идея. Индекс проигнорируют. SELECT * FROM logs WHERE DATE(created_at) = '2023-10-01'; -- А вот так — уже умно. Использует и индекс, и твои мозги. SELECT * FROM logs WHERE created_at >= '2023-10-01' AND created_at < '2023-10-02'; -
Статистика говно. Оптимизатор — не ясновидящий, он смотрит на цифры. Если статистика собрана давно и таблица с тех пор выросла в десять раз, он будет ошибаться в расчётах, как школьник на контрольной. Доверия к таким данным — ноль ебать.
-
Таблица маленькая или ты сам настаиваешь. Для таблицы на пять строк индексы — это овердохуища ненужных телодвижений. Или ты мог явно впихнуть в запрос подсказку
/*+ FULL(table_name) */, вот он и слушается.
Как это проверить, спросишь? Да элементарно, чувак! Запусти EXPLAIN и посмотри, что он там намудрил.
EXPLAIN ANALYZE
SELECT * FROM large_table WHERE low_selectivity_column = 'common_value';
-- И если в выводе увидишь гордое "Seq Scan" (последовательное сканирование) вместо "Index Scan" — всё, попал. Хитрая жопа оптимизатора тебя переиграла.