web-dev-qa-db-ja.com

PostgreSQL LIKEクエリパフォーマンスのバリエーション

私のデータベース内の特定のテーブルへのLIKEクエリに関して、応答時間に非常に大きな変動を見てきました。場合によっては200〜400ミリ秒以内に結果が得られます(非常に許容範囲内)が、結果を返すのに30秒もかかる場合があります。

LIKEクエリはリソースを大量に消費することは理解していますが、応答時間がこのように大きく異なる理由はわかりません。 owner1フィールドにbtreeインデックスを作成しましたが、LIKEクエリに役立つとは思いません。誰にもアイデアはありますか?

サンプルSQL:

SELECT gid, owner1 FORM parcels
WHERE owner1 ILIKE '%someones name%' LIMIT 10

私も試しました:

SELECT gid, owner1 FROM parcels
WHERE lower(owner1) LIKE lower('%someones name%') LIMIT 10

そして:

SELECT gid, owner1 FROM parcels
WHERE lower(owner1) LIKE lower('someones name%') LIMIT 10

同様の結果。
テーブル行数:約95,000。

101
Jason

FTSはLIKEをサポートしていません

以前に受け入れられた回答 が間違っていました。 全文検索 全文索引は、LIKE演算子に対してnotであり、独自の演算子を持ち、任意の文字列には機能しません。辞書とステミングに基づいてwordsで動作します。 It does support prefix matching for words、ただしLIKE演算子ではサポートしません:

LIKEのトライグラムインデックス

追加モジュール _pg_trgm_ をインストールします。このモジュールは、 GINおよびGistトリグラムインデックスの演算子クラスを提供しますすべてのLIKEおよびILIKEパターンをサポートし、左アンカーのパターンだけではありません:

インデックスの例:

CREATE INDEX tbl_col_gin_trgm_idx  ON tbl USING gin  (col gin_trgm_ops);

または:

CREATE INDEX tbl_col_Gist_trgm_idx ON tbl USING Gist (col Gist_trgm_ops);

クエリの例:

_SELECT * FROM tbl WHERE col LIKE '%foo%';   -- leading wildcard
SELECT * FROM tbl WHERE col ILIKE '%foo%';  -- works case insensitively as well_

トライグラム?短い弦はどうですか?

インデックス値に3文字未満の単語が引き続き機能します。 マニュアル:

文字列に含まれるトライグラムのセットを決定するときに、各Wordには接頭辞2つのスペースと接尾辞1つのスペースがあると見なされます。

そして、3文字未満の検索パターン? マニュアル:

LIKEと正規表現の両方の検索では、抽出可能なトライグラムのないパターンは、全インデックススキャンに縮退することに注意してください。

つまり、インデックス/ビットマップインデックススキャンは引き続き機能します(準備されたステートメントのクエリプランは壊れません)。パフォーマンスが向上するわけではありません。通常、大きな損失はありません。1文字または2文字の文字列はほとんど選択的ではないため(基礎となるテーブルの数パーセント以上)、インデックスのサポートは最初からパフォーマンスを向上させません。


_text_pattern_ops_プレフィックス一致

ちょうどleft-anchoredパターン(先頭のワイルドカードなし)の場合、btreeインデックスに適した operator class を使用して最適な値を取得します:_text_pattern_ops_または_varchar_pattern_ops_。標準のPostgresの両方の組み込み機能。追加のモジュールは不要です。パフォーマンスは似ていますが、インデックスははるかに小さくなっています。

インデックスの例:

CREATE INDEX tbl_col_text_pattern_ops_idx ON tbl(col text_pattern_ops);

クエリの例:

_SELECT * FROM tbl WHERE col LIKE 'foo%';  -- no leading wildcard_

Or'C'ロケール(実質的にnoロケール)でデータベースを実行する必要がある場合、すべてがバイト順に従ってソートされます。とにかく、デフォルトの演算子クラスを持つプレーンなbtreeインデックスが仕事をします。

Dba.SEのこれらの関連する回答の詳細、説明、例、およびリンク:

260

おそらく高速なものは、インデックスを使用できるような大文字と小文字を区別するアンカーパターンです。つまり、一致文字列の先頭にワイルドカードがないため、エグゼキューターはインデックス範囲スキャンを使用できます。 ( ドキュメント内の関連するコメントはこちら )Lowerおよびilikeは、その目的のためにインデックスを特別に作成しない限り、インデックスを使用する能力も失います( 機能インデックス を参照) 。

フィールドの中央で文字列を検索する場合は、 full text または trigram indexs を調べる必要があります。それらの1つはPostgresコアにあり、もう1つはcontribモジュールにあります。

7
Ants Aasma

PostgreSQLの異なるタイプのインデックスである Wildspeed をインストールできます。 Wildspeedは%Word%ワイルドカードで動作しますが、問題ありません。欠点はインデックスのサイズです。これは非常に大きくなる可能性があります。

4
Frank Heikens

PostgresqlでのLIKEクエリのパフォーマンスを改善するには、以下のクエリを実行してください。大きなテーブル用にこのようなインデックスを作成します。

CREATE INDEX <indexname> ON <tablename> USING btree (<fieldname> text_pattern_ops)
2
Noyal

最近、200000レコードを含むテーブルで同様の問題が発生しました。繰り返しLIKEクエリを実行する必要があります。私の場合、検索される文字列は修正されました。他のフィールドはさまざまでした。そのため、私は書き直すことができました:

SELECT owner1 FROM parcels
WHERE lower(owner1) LIKE lower('%someones name%');

なので

CREATE INDEX ix_parcels ON parcels(position(lower('someones name') in lower(owner1)));

SELECT owner1 FROM parcels
WHERE position(lower('someones name') in lower(owner1)) > 0;

クエリが高速で戻ってきて、インデックスがEXPLAIN ANALYZE

 Bitmap Heap Scan on parcels  (cost=7.66..25.59 rows=453 width=32) (actual time=0.006..0.006 rows=0 loops=1)
   Recheck Cond: ("position"(lower(owner1), 'someones name'::text) > 0)
   ->  Bitmap Index Scan on ix_parcels  (cost=0.00..7.55 rows=453 width=0) (actual time=0.004..0.004 rows=0 loops=1)
         Index Cond: ("position"(lower(owner1), 'someones name'::text) > 0)
 Planning time: 0.075 ms
 Execution time: 0.025 ms
1
Stephen Quan

Likeクエリは、おそらく作成したインデックスを使用できません。

1)LIKE基準はワイルドカードで始まります。

2)LIKE基準で関数を使用しました。

0
Asaph

DjangoORMは、すべてのLIKEクエリにUPPER(text)を使用して大文字と小文字を区別しない傾向があり、

UPPER(column::text)にインデックスを追加すると、他のものとは異なり、システムが大幅に高速化されます。

先頭の%に関しては、はい、インデックスを使用しません。優れた説明については、このブログを参照してください。

https://use-the-index-luke.com/sql/where-clause/searching-for-ranges/like-performance-tuning

0
MrE