В каком случае при наличии индекса будет полное сканирование таблицы (full scan)?

Ответ

Оптимизатор СУБД может выбрать полное сканирование таблицы (FULL TABLE SCAN) вместо использования индекса в следующих основных случаях:

  1. Низкая селективность условия: Если условие WHERE охватывает большую часть таблицы (например, >15-20% строк). Сканирование по индексу с последующими обращениями к таблице (TABLE ACCESS BY INDEX ROWID) становится дороже, чем прямое последовательное чтение.

    -- Индекс по is_active вряд ли будет использован, если 90% строк active
    SELECT * FROM users WHERE is_active = TRUE;
  2. Отсутствие подходящего префикса индекса: Для составного индекса (A, B) запрос с фильтром только по B не сможет его использовать эффективно.

  3. Использование функций или модификаторов столбца: Индекс по 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';
  4. Неактуальная статистика: Если статистика по таблице устарела, оптимизатор может ошибиться в оценке стоимости.

  5. Подсказка оптимизатору или маленькая таблица: Для очень маленьких таблиц сканирование всегда дешевле. Также можно явно указать /*+ FULL(table_name) */.

Как проверить? Используйте EXPLAIN (или EXPLAIN ANALYZE):

EXPLAIN ANALYZE
SELECT * FROM large_table WHERE low_selectivity_column = 'common_value';
-- В плане выполнения будет видно "Seq Scan" вместо "Index Scan"

Ответ 18+ 🔞

Да ты посмотри, что творится! Оптимизатор этой вашей СУБД — он же не дурак, он, блядь, считает. И если ему кажется, что просканировать всю таблицу целиком будет быстрее, чем прыгать по индексу, он так и сделает. Сам от себя охуеешь, когда ждёшь быстрого индекса, а он тебе в ответ — полное сканирование, ёпта.

Вот основные причины, почему эта мартышлюшка так поступает:

  1. Условие в WHERE слишком широкое. Если ты выбираешь, условно, 90% строк из таблицы, то какой смысл лазить по индексу, а потом ещё по таблице бегать за каждой строчкой? Дешевле сразу всё прочитать пачкой. Это как вместо того, чтобы по номеру квартиры искать человека в доме, зайти и крикнуть: «Эй, все выходите!» — если выходить должны почти все.

    -- Индекс по is_active тут просто накрылся медным тазом, если почти все пользователи активны.
    SELECT * FROM users WHERE is_active = TRUE;
  2. Ты лезешь не с того конца. Есть у тебя составной индекс (A, B). А ты в запросе фильтруешь только по B. Ну и как он его использует, этот индекс? Никак. Это всё равно что искать книгу в библиотеке только по последней странице. Хуй с горы, а не поиск.

  3. Ты изгаляешься над столбцом. Навесил на него функцию в условии — и всё, приехали. Индекс по 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';
  4. Статистика говно. Оптимизатор — не ясновидящий, он смотрит на цифры. Если статистика собрана давно и таблица с тех пор выросла в десять раз, он будет ошибаться в расчётах, как школьник на контрольной. Доверия к таким данным — ноль ебать.

  5. Таблица маленькая или ты сам настаиваешь. Для таблицы на пять строк индексы — это овердохуища ненужных телодвижений. Или ты мог явно впихнуть в запрос подсказку /*+ FULL(table_name) */, вот он и слушается.

Как это проверить, спросишь? Да элементарно, чувак! Запусти EXPLAIN и посмотри, что он там намудрил.

EXPLAIN ANALYZE
SELECT * FROM large_table WHERE low_selectivity_column = 'common_value';
-- И если в выводе увидишь гордое "Seq Scan" (последовательное сканирование) вместо "Index Scan" — всё, попал. Хитрая жопа оптимизатора тебя переиграла.