web-dev-qa-db-ja.com

citext列の式インデックスが無視されました、なぜですか?

約32M行のRDSで実行されています。

PostgreSQL 11.4 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-11), 64-bit

また、約800万行のmacOSでローカルにテストします。

PostgreSQL 11.5 on x86_64-Apple-darwin16.7.0, compiled by Apple LLVM version 8.1.0 (clang-802.0.42), 64-bit

タイプがcitextのold_valueという列があります。私はすでにこれについて尋ねましたが、wayを途中の私の発見ステップの多くに投稿しました。ここに私が要点を得ることを望んでいる煮詰めたバージョンがあります。

バックグラウンド

Old_valueという名前のcitextフィールドを含む、32M行の成長するrecord_changes_log_detailという名前のフィールド変更ログテーブルがあります。

データはveryskeweedです。ほとんどの値は12文字未満であり、5,000を超えるものもあります。

Postgresは、Bツリーエントリが2172文字に制限されているというエラーで大きな値をチョークします。したがって、Bツリーの場合、ソース値を部分文字列化する必要があると思います。

ユーザーの主な関心は、=検索、starts-with検索、場合によっては、contains-this-substring検索です。つまり= string%および%string%

ゴール

プランナーが使用する検索をサポートするインデックスを作成します。

試して失敗した

値が長いため、まっすぐなBツリーを構築できない場合があります。

このような式Bツリーは構築されますが、使用されません

CREATE INDEX record_changes_log_detail_old_value_ix_btree
    ON  record_changes_log_detail 
    USING btree (substring(old_value,1,1024));

Text_pattern_optsを追加しても効果はありません。

CREATE INDEX record_changes_log_detail_old_value_ix_btree
    ON  record_changes_log_detail 
    USING btree (substring(old_value,1,1024) text_pattern_opts);

試して部分的に動作

ハッシュインデックスは機能しますが、同等である場合のみです。 (それは缶に言うように。)

これは私が成功に至った最も近いものです。

CREATE INDEX record_changes_log_detail_old_value_ix_btree
    ON record_changes_log_detail 
    USING btree (old_value citext_pattern_ops);

これは品質では機能しますが、LIKEでは機能しません。 PG 11のリリースノートでは、LIKEでも機能するはずであるとしています。

https://www.postgresql.org/docs/11/release-11.html

「仕事」とは、「インデックスが使用されている」という意味です。

このアプローチでは、うまくサブストリングすることができませんでした。

この状況では、人々はCitextフィールドで何をしますか?

4
Morris de Oryx

このような長い列に完全にインデックスを付けるのは珍しいことです。

3つのアイデア:

  1. 次のようにクエリを変更します。

    _WHERE substring(old_value, 1, 100) LIKE substring(pattern, 1, 100)
      AND old_value LIKE pattern
    _

    patternは、_'string%'_のようなパターン文字列です。)

    次に、substring(old_value, 1, 100)のbツリーインデックスを使用できます(もちろん、パターンがワイルドカード文字で始まっていない場合)。

  2. 正確な要件に応じて(自然言語テキストで完全な単語または単語のプレフィックスを検索するかどうか)、 フルテキスト検索 が適切な解決策になる場合があります。

  3. 別のオプションはもちろんトライグラムインデックスです。

    _CREATE INDEX ON record_changes_log_detail USING gin (old_value gin_trgm_ops);
    _

    これには、_pg_trgm_拡張機能をインストールする必要があります。

    このようなインデックスは、ワイルドカードで始まる検索パターンでも機能します。良好なパフォーマンスを得るには、検索文字列に最小の長さを適用します。

2
Laurenz Albe

質問に回答しない回答を投稿するのではなく、質問を編集してください。

substring(old_value,1,1024)にインデックスを作成すると、クエリにsubstring(old_value,1,1024)が含まれる場合にのみ、そのインデックスを使用できます。

部分文字列の内部について十分な洞察力があれば、_old_value='foo'_がsubstring(old_value,1,1024)='foo'(つまり、これとは対照的)であることを理論的に証明することは可能ですが、PostgreSQLはそれを証明する。このような証明が不要な方法でクエリを記述する必要があります。

3
jjanes

この質問を締めくくりたいと思います。ローレンツアルベからの提案に続き、Postgresのトライグラム実装を試してみました。彼らが支配する!

DROP INDEX IF EXISTS record_changes_log_detail_old_value_ix_tgrm;
CREATE INDEX record_changes_log_detail_old_value_ix_tgrm
    ON record_changes_log_detail 
    USING gin (old_value gin_trgm_ops);

ここでcitextを使用しているときの秘訣は、次のように値を:: textにキャストすることです。

select * from record_changes_log_detail 
where old_value::text LIKE '%Gold Kerrison Neuro%';

これをExplain分析で実行すると、インデックスが使用されていることが確認されます。 =検索にLIKEを使用する必要があることに気づきましたが、それで問題ありません。

0
Morris de Oryx