PostgreSQLは、実際には配列列のGINインデックスをサポートしています。残念ながら、には使用できないようでNOT ARRAY[...] <@ indexed_col
あり、GIN
インデックスはとにかく頻繁に更新されるテーブルには適していません。
デモ:
CREATE TABLE arrtable (id integer primary key, array_column integer[]);
INSERT INTO arrtable(1, ARRAY[1,2,3,4]);
CREATE INDEX arrtable_arraycolumn_gin_arr_idx
ON arrtable USING GIN(array_column);
-- Use the following *only* for testing whether Pg can use an index
-- Do not use it in production.
SET enable_seqscan = off;
explain (buffers, analyze) select count(id)
from arrtable
where not (ARRAY[1] <@ arrtable.array_column);
残念ながら、これは書かれているようにインデックスを使用できないことを示しています。条件を否定しない場合は、それを使用できるため、検索要素を含む行を検索してカウントできます(を削除することにより)NOT
。
インデックスを使用して、ターゲット値を含むエントリをカウントし、その結果をすべてのエントリのカウントから差し引くことができます。PostgreSQL(9.1以前)ではテーブル内のすべての行の実行が非常に遅く、順次スキャンが必要なためcount
、これは実際には現在のクエリよりも遅くなります。9.2では、bツリーインデックスがオンid
になっている場合、インデックスのみのスキャンを使用して行をカウントできる可能性があります。その場合、これは実際には問題ない可能性があります。
SELECT (
SELECT count(id) FROM arrtable
) - (
SELECT count(id) FROM arrtable
WHERE (ARRAY[1] <@ arrtable.array_column)
);
Pg 9.1以下の元のバージョンよりもパフォーマンスが低下することが保証されています。これは、seqscanに加えて、元のバージョンではGINインデックススキャンも必要になるためです。これを9.2でテストしましたが、カウントにインデックスを使用しているように見えるので、9.2で調べる価値があります。ささいなダミーデータを使用すると、次のようになります。
drop index arrtable_arraycolumn_gin_arr_idx ;
truncate table arrtable;
insert into arrtable (id, array_column)
select s, ARRAY[1,2,s,s*2,s*3,s/2,s/4] FROM generate_series(1,1000000) s;
CREATE INDEX arrtable_arraycolumn_gin_arr_idx
ON arrtable USING GIN(array_column);
このようなGINインデックスは、更新の速度を大幅に低下させ、そもそも作成に非常に時間がかかることに注意してください。テーブルのように、大幅に更新されるテーブルには適していません。
さらに悪いことに、このインデックスを使用するクエリは、元のクエリの最大2倍、同じデータセットで最大で半分の時間がかかります。ARRAY[1]
元のクエリの-4秒対2秒のように、インデックスがあまり選択的でない場合は最悪です。インデックスが非常に選択的である場合(つまり、のように一致が多くない場合ARRAY[199]
)、元の3秒に対して約1.2秒で実行されます。このインデックスは、このクエリに使用する価値がありません。
ここでのレッスンは?場合によっては、正しい答えは単にシーケンシャルスキャンを実行することです。
ヒット率には影響しないため、@ debenhurが提案するようにトリガーを使用してマテリアライズドビューを維持するか、配列を反転してエントリにないパラメーターのリストにして、 GiSTインデックスを次のように使用できるようにします。 @maniekが提案します。