値が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 NULLor 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の潜在能力を最大限に引き出すことができない松葉杖です。


素晴らしい答え、アーウィンに感謝します。2回投票できないのが残念です。
Simone Carletti、2012年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.