0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

SQLパフォーマンス詳解 2章: where句_9. LIKEフィルタに対するインデックス

Posted at

2章: where句

9. LIKEフィルタに対するインデックス

1. ワイルドカード位置がすべてを決める

  • LIKE 'abc%'(前方一致)
    B-treeでINDEX RANGE SCAN可能(インデックス有効)
  • LIKE '%abc'(後方一致)
    ➡ B-treeでは開始位置が分からず 通常はインデックス不可
  • LIKE '%abc%'(部分一致/「全文検索的」)
    ➡ B-treeでは基本的に インデックス不可/フルスキャン

2. アクセス述語 vs フィルタ述語(重要)

  • アクセス述語: インデックス走査の開始・終了点を決める(スキャン範囲を狭める)
  • フィルタ述語: リーフノード走査後に評価される(スキャン範囲は狭めない)

➡ LIKE 式は「最初のワイルドカードの前まで」がアクセス述語、それ以降はフィルタ述語になる。


3. バインドパラメータとの関係

  • WHERE col LIKE :p のとき、最適化段階で :p の中身が不明なため多くのDBは 「先頭ワイルドカードは無い(前方一致)」と仮定する
     
  • 実際に :p = '%foo%' が渡されると仮定(foo%)と実態(%foo%)がズレ、期待どおりにインデックスが使われない/実行計画が合わない問題が発生する
     
  • 直書きリテラルだとDBは正しく判断するが、毎回別SQL扱いになり最適化コストとSQLインジェクションリスクが増す

4. 対策(性能・安全性を両立する選択肢)

  • 最も簡単で確実に高速化:
    検索要件が前方一致にできるなら abc% を使う(アプリ側で入力を整形)
     
  • 全文/部分一致が必要な場合:
    専用の全文検索インデックスを使う(Oracle Text、Postgres GIN/pg_trgm、外部全文検索エンジン)
     
  • 後方一致(%TERM)を高速化したい場合:
    文字列を反転して関数インデックスを作る
    reverse(col) に関数インデックスを張り、reverse(col) LIKE 'MRET%' にする)
     
  • SQL設計の工夫:
    検索意図ごとにSQLを分けて(前方一致用・部分一致用など)明示的に使い分けることで、バインドを保ちつつ最適化ミスを避ける
     
  • 避けるべき:
    漠然と LIKE :p を渡して「DBが何でも最適化してくれる」と期待すること

5. DB固有の注意点

PostgreSQL は、バインドパラメータ付き LIKE の場合
“前方一致である”とは仮定せず、”部分一致である”と仮定するため、B-tree インデックスが使用されない。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?