Несподіване сканування Seq при виконанні запиту проти булевого значення зі значенням NULL


10

У мене стовпець бази даних називається, auto_reviewде тип стовпця boolean. Існує індекс для цього поля, створений за допомогою ORM ActiveRecord.

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 - це милиці, які регулярно не вдається отримати повний потенціал з ваших RDBMS.


Блискуча відповідь, дякую Ервіне. Мені сумно, що не можу підтвердити це двічі.
Сімоне Карлетті
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.