web-dev-qa-db-ja.com

postgres: '%foo%'のようなSELECT文字列のインデックス。

次の形式の高価で定期的なPostgreSQL 9.3クエリがあります。

SELECT * from mytable where name NOT LIKE '%foo%';

fooは、実際には変更されない定数です。クエリはテーブルスキャンを必要とするため、コストがかかります。クエリに実際に一致するのは数行のみであるため、部分インデックスを使用してクエリを高速化したいと考えています。

CREATE INDEX foo_idx ON mytable ((name NOT LIKE '%foo%')) WHERE name NOT LIKE '%foo%';

ただし、クエリでEXPLAINを実行すると、新しいインデックスに依存するのではなく、引き続き順次スキャンが使用されます。 何が悪いのですか?

CREATE INDEXステートメントの括弧で囲まれた式の句の中でいくつかの異なる式を試しましたが、何も役に立ちません:

CREATE INDEX foo_idx ON mytable (name) WHERE name NOT LIKE '%foo%';
CREATE INDEX foo_idx ON mytable ((name LIKE '%foo%')) WHERE name NOT LIKE '%foo%';

また、インデックスのWHERE句を削除しようとしましたが、それも役に立ちません。

5
Mike Curtiss

インデックスはそのまま機能します。 @ jjanesで指摘 のように、ANALYZEを実行するだけで済みます。ただし、インデックスを変更することをお勧めします。定義内の式は役に立ちません。

CREATE INDEX foo_idx ON intentions ((data LIKE '%foo%')) WHERE data NOT LIKE '%foo%';

式は常にFALSEであり、役に立たないノイズです。少し安い同等のインデックスに単純化します。

CREATE INDEX foo_idx ON intentions ((FALSE)) WHERE data NOT LIKE '%foo%';

または、さらに便利なことに、何か便利なものを入れてください。とにかく、各インデックスエントリはデータにMAXALIGN(通常8バイト)を割り当て、index行あたりのオーバーヘッドはさらに12です。バイト(およびbtreeインデックスのデフォルトのfillfactorは90%です)。インデックスを有効に使用して、他のクエリを支援することもできます。同じ部分インデックスを使用する可能性のある他のクエリがある場合は?

最大8バイトの価値のあるもの(1つまたは2つのint4列など)を配置すると、インデックスはまったく同じサイズとパフォーマンスを保持しますが、より多くの可能なユースケースを提供します。列(の1つ)が更新に関係している場合、追加の保守コストがかかります。それでも、何か役に立つものがあれば、それを使用してください。 JOIN/WHEREまたはORDER BY句で使用される追加の列。または、インデックスのみのスキャンを有効にする列:

CREATE INDEX foo_idx ON intentions (<useful_column(s)>)
WHERE data NOT LIKE '%foo%';
2

少し異なるアプローチを試すことができます。

CREATE INDEX foo_idx ON mytable (strpos(name,'foo'));

SELECT * from mytable where strpos(name,'foo') = 0;

式のインデックス

1
MickyT

NOT LIKEに問題があります。 NOTなしでこのクエリを想像してください。インデックスをウォークして、一致する値の間隔(インデックスはソートされたリスト)を見つけることができます。 NOTは、一致する間隔の前後の両方で、他のすべてが必要であることを意味します。それはそれを行うかなり特別なオプティマイザです。

doに「foo」が含まれるすべての行を特定するためにインデックスを反転させ、その結果をNOT EXISTS(または同等のもの)を使用して、目的の出力を見つけます。ただし、テーブルスキャンは引き続き表示されます。

または、列IsFooをテーブルに追加します。これをテーブル書き込みに入力します(おそらく、永続的な計算列を使用しますか?)。インデックスとフィルターにIsFooを含めます。

1
Michael Green

適切なクエリプランを有効にするには、ANALYZEを実行する必要がありました。これが行われると、すべてが魅力のように機能しました。私の最終的なインデックスは次のようになります:

CREATE INDEX foo_idx ON intentions ((data LIKE '%foo%')) WHERE data NOT LIKE '%foo%';
1
Mike Curtiss