Неожиданное сканирование Seq при выполнении запроса к логическому значению со значением NULL

10

У меня есть столбец базы данных под названием, auto_reviewгде тип столбца boolean. Для этого поля есть индекс, созданный с помощью ActiveRecord ORM.

CREATE INDEX index_table_on_auto_renew ON table USING btree (auto_renew);

Когда я запрашиваю в поле логическое значение, PG использует индекс, как и ожидалось.

EXPLAIN for: SELECT "table".* FROM "table"  WHERE "table"."auto_renew" = 'f'
                                          QUERY PLAN
----------------------------------------------------------------------------------------------
 Bitmap Heap Scan on table  (cost=51.65..826.50 rows=28039 width=186)
   Filter: (NOT auto_renew)
   ->  Bitmap Index Scan on index_domains_on_auto_renew  (cost=0.00..44.64 rows=2185 width=0)
         Index Cond: (auto_renew = false)
(4 rows)

Когда значение равно NULL, используется последовательное сканирование.

EXPLAIN for: SELECT "table".* FROM "table"  WHERE "table"."auto_renew" IS NULL
                           QUERY PLAN
----------------------------------------------------------------
 Seq Scan on table  (cost=0.00..1094.01 rows=25854 width=186)
   Filter: (auto_renew IS NULL)
(2 rows)

Мне любопытно узнать причину этого выбора.

Симона Карлетти
источник

Ответы:

19

Как правило, col IS NULLэто возможный кандидат для поиска по индексу b-дерева (по умолчанию). Руководство :

Кроме того, условие IS NULLили IS NOT NULLстолбец индекса может использоваться с индексом B-дерева.

Чтобы получить подтверждение, отключите последовательное сканирование (только в тестовой сессии!):

SET enable_seqscan = OFF;

Я цитирую руководство здесь :

enable_seqscan (boolean)

Включает или отключает использование планировщиком запросов типов планов последовательного сканирования. Невозможно полностью отключить последовательное сканирование, но отключение этой переменной не позволяет планировщику использовать один, если есть другие доступные методы. По умолчанию включено.

Тогда попробуйте еще раз:

EXPLAIN ANALYZE SELECT * FROM tbl WHERE auto_renew IS NULL;

Это, вероятно, приведет к сканированию индекса растрового изображения, которое будет медленнее, чем последовательное сканирование таблицы.

Сбросьте или закройте сеанс (параметр является локальным для сеанса).

RESET enable_seqscan;

Индексы по booleanстолбцам полезны только в определенных случаях. Планировщик использует индекс, только если ожидает, что он будет быстрее. Расчеты основаны на ваших настройках стоимости и статистике, собранной ANALYZE. Если значительная часть таблицы соответствует вашим условиям (около 5% или более, это зависит от ситуации), вместо этого обычно быстрее выполнить полное сканирование таблицы.

Это оставляет редкое значение в booleanстолбце в качестве единственного полезного кандидата для простого индекса. И для этого, как правило, более эффективно создать (более специализированный) частичный индекс - который дешевле поддерживать, меньше, быстрее и легче использовать, если условие запроса совпадает.

Если у вас есть много запросов, ищущих строки с, auto_renew IS NULLи NULLслучай не очень распространен (и / или вам нужен определенный порядок сортировки), тогда этот индекс поможет быстро найти / отсортировать эти строки:

CREATE INDEX index_tbl_tbl_id_auto_renew_null ON tbl (tbl_id)
WHERE auto_renew IS NULL;

Условие частичного индекса должно повторяться в WHEREпредложении запроса более или менее точно, чтобы планировщик запросов понял, что индекс применим.

Индексированный столбец ( tbl_id) - это произвольный выбор. Важной частью является WHEREпункт. Этот конкретный индекс будет наиболее эффективным для запросов с ORDER BY tbl_idдополнительным фильтром или присоединением tbl_id. Вы можете сделать это многоколоночным индексом . Булевы столбцы часто более полезны в сочетании с другими.

Кроме того: ORM - это костыли, которые регулярно не в состоянии полностью раскрыть потенциал вашей СУБД.

Эрвин Брандштеттер
источник
Блестящий ответ, спасибо, Эрвин. Мне грустно, я не могу объявить это дважды.
Симона Карлетти