PostgreSQLはインデックスにnullを使用できますか?


10

私はそれを言っているこの本を読んでいます

データベースは、Indexed_Col IS NOT NULLがカバーする範囲が大きすぎて役に立たないと想定しているため、データベースはこの状態からインデックスに移動しません。

この本は10年以上前のものであると認識していますが、すでに非常に有用であることが証明されています。

さらに、クエリを実行EXPLAIN ANALYZESELECTているときに、自分のインデックスがまったく使用されていないことがわかりました。

したがって、私の質問は:

列が "NOT NULL"を含む列を持つテーブルがあり、この列をカバーするインデックスが存在する場合、このインデックスは、列がクエリの一部であるテーブルのクエリで使用されますか?

お気に入り:

CREATE TABLE my_table(
a varchar NOT NULL
);

CREATE INDEX ix_my_table ON my_table(a);

SELECT a from my_table;

回答:


9

PostgreSQLは確かにのインデックスを使用できますIS NOT NULL。その条件についてのクエリプランナーの想定もわかりません。

列(pg_statistic.stanullfrac)のnullの割合が低く、インデックスがクエリに対して有効に選択的であることを示唆している場合、PostgreSQLはインデックスを使用します。

私はあなたが何を言おうとしているのか理解できません:

これが正しい場合、「NOT NULL」として定義された列のインデックスは、その列を使用するクエリで使用されないことを理解していますか?

確かに、インデックスは列のIS NOT NULL条件に使用されませんNOT NULL。それは常に100%の行に一致するため、seqscanはほとんど常に高速です。

インデックスがクエリの行の大部分を除外しない場合、PostgreSQLはインデックスを使用しません。唯一の例外は、単一のインデックスでカバーされる列のセットを、インデックスの列と一致する順序で要求する場合です。その場合、PostgreSQLはインデックスのみのスキャンを行う可能性があります。たとえば、インデックスがt(a, b, c)あり、あなたが:

select a, b FROM t ORDER BY a, b, c;

行がフィルターで除外されていなくても、PostgreSQLはインデックスを使用する場合があります。これは、インデックスを読み取るだけで、ヒープの読み取りをスキップしたり、並べ替えを回避したりできるためです。



1
また、Nullable列でも、条件付きクエリWHERE column IS NOT NULLはインデックスを使用しない場合があります。これは、この本で述べているように、「範囲が広すぎて役に立たない」ためです。値の90%がnullでない場合、seqscanもおそらく高速になります。
ypercubeᵀᴹ

丁度。可能性がありますが、テーブルの大部分がnullの場合のみです。多くの場合、この場合、部分インデックスはとにかくより良い選択です。
クレイグリンガー

はい。私が理解しているように、「範囲が広すぎる」という部分はインデックスを指しているが、一般的なインデックスではなく、特定の条件に関してだと言っていました。
ypercubeᵀᴹ

2
@FuriousFolderへえ、ここには否定が多すぎます。PostgreSQLは、そのインデックスが句の他の部分や結合フィルターなどにも役立つか、順序付けされたインデックスのみのスキャンに使用できる場合を除いてNOT NULLIS NOT NULLクエリの列のインデックスを使用しませんWHERE。つまり、列の冗長性IS NOT NULLを完全に無視し、NOT NULL他の詳細に基づいてインデックスの使用を選択します。(編集、インデックスのみのスキャンの再参照を参照)。
クレイグリンガー

2

クレイグの徹底した答えに加えて、私はあなたが参照する本の表紙が言うことを付け加えたかった:

Oracle、DB2、SQL Serverをカバー

そのため、特にPostgreSQLに関する素晴らしいアドバイスになるとは信じていません。すべてのRDBMSは驚くほど異なる可能性があります。

元の質問について少し混乱していますが、本のセクションが100%正しくないことを示す例を次に示します。さらに混乱を避けるため、関連する段落全体を以下に示します。Googleブック検索確認できます。

データベースは、Indexed_Col IS NOT NULLがカバーする範囲が大きすぎて役に立たないと想定しているため、データベースはこの状態からインデックスに移動しません。まれなケースとして、null以外の値が存在することは非常にまれであるため、考えられるすべてのnull以外の値に対するインデックス範囲スキャンが有益です。そのような場合、すべての可能な値の範囲の安全な下限または上限を把握できれば、Positive_ID_Column> -1またはDate_Column> TO_DATE( '0001/01/01'などの条件で範囲スキャンを有効にすることができます。 、「YYYY / MM / DD」)。

Postgresは実際に(以下の不自然なケースで)インデックスを使用してIS NOT NULL、推奨されてPositive_ID_Column > -1いるのような範囲スキャンクルージを追加することなく、クエリを満たすことができます。この特定のケースでPostgresがこのインデックスを選択する理由については、Craigの質問へのコメントと、部分インデックスの使用に関するメモを参照してください。

CREATE TABLE bar (a int);
INSERT INTO bar (a) SELECT NULL FROM generate_series(1,1000000);
INSERT INTO bar (a) VALUES (1);
CREATE INDEX bar_idx ON bar (a);

EXPLAIN ANALYZE SELECT * FROM bar WHERE a IS NOT NULL;
                                                QUERY PLAN                                                    
------------------------------------------------------------------------------------------------------------------
 Index Only Scan using bar_idx on bar  (cost=0.42..8.44 rows=1 width=4) (actual time=0.094..0.095 rows=1 loops=1)
   Index Cond: (a IS NOT NULL)
   Heap Fetches: 1
 Total runtime: 0.126 ms
(4 rows)

ちなみにこれはPostgres 9.3ですが、「インデックスのみのスキャン」を使用しない場合でも、結果は9.1とほぼ同じだと思います。

編集:元の質問を明確にしたと思いますが、Postgresが次のような簡単な例でインデックスを使用していないのはなぜか疑問に思っているようです。

CREATE TABLE my_table(
a varchar NOT NULL
);

CREATE INDEX ix_my_table ON my_table(a);

SELECT a from my_table;

おそらくテーブルに行がないためです。したがって、テストデータとを追加しANALYZE my_table;ます。


この本の説明では(強調は私のものです)、「著者Dan Tow は、使用しているSQLまたはデータベースプラットフォームの複雑さに関係なく、迅速かつ体系的に、最適な実行計画を見つけるために開発した時間節約方法を概説しています。」おそらく、質問の#1を見落としている可能性があります。つまり、クエリがインデックス条件として使用するのではなく、列がとして定義さNOT NULLていますIS NOT NULL。これはあなたが参照したコメントにありますが、質問を更新して含めます。
FuriousFolder

さらに、本自体は言語にとらわれない:DMBS固有の部分はクエリプランの表示に関するものだけであり、Postgresはこれを非常に単純にしています:)
FuriousFolder

1
@FuriousFolder列はNOT NULLとして定義されていますが、この部分(質問では、本から):「そのIndexed_Col IS NOT NULLは...をカバーしています」は、列定義ではなくwhere条件を参照しています。それは文脈の外にあるので、それを確認するのは難しいですが。おそらく、本の(前の)段落全体を含める必要があります。
ypercubeᵀᴹ

-1

クエリまたはサンプルデータを投稿していません。しかし、インデックスが使用されない最も一般的な理由は、ボリュームに関係しています。

インデックスは、列を行の場所に変換する電話帳のようなものです。数行のみを検索する場合は、電話帳の各行を検索してから、メインテーブルの行を検索するのが理にかなっています。

ただし、数行を超える場合は、電話帳をスキップし、メインテーブルのすべての行を反復処理する方が安上がりです。私の経験では、転換点は約100行です。


「インデックスは、列を行の場所に変換する電話帳のようなものです。数行しか検索しない場合は、電話帳の各行を検索してから、メインテーブルの行を検索するのが理にかなっています。」実際、インデックスは、インデックスが作成された電話帳が更新されるたびに更新される小さな電話帳のようなものです。小さな電話帳を開くと、そのインデックスの状態が示すすべての情報が見つかるはずです。たとえば、インデックステーブルで「frank」という名前のすべての人:CREATE INDEX ix_frank ON people(name) WHERE name ='frank'
FuriousFolder 2015

これにより、「小さな電話帳」全体をメモリに読み込むことができるため、インデックスのみのスキャンをはるか 高速に実行できます。これは、数百万行のテーブルでは実現できません。
FuriousFolder 2015

@FuriousFolder:インデックスのみのスキャンについて説明しています。しかし、OPは彼のインデックスが使用されていないと述べています。これは、インデックスのみのスキャンがクエリを満たす場合には起こりません。
Andomar

Andomar ...私 OP です。私の目標はまさにそれです。このクエリでインデックスのみのスキャンを使用できるようにします。postgres 列の定義を含む列でインデックスを使用できるとCraigが説明したので、私はそれ以来、それを達成しました NOT NULL
FuriousFolder
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.