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句_4.関数

Posted at

2章:where句

4.関数

結論

  • UPPER()LOWER()WHERE句の列側に使うと、通常のインデックスは使われない
  • 大文字・小文字を区別しない検索を パフォーマンスを落とさずに行うには、関数インデックスが必要
  • 関数インデックス作成後は、統計情報の更新が必須

なぜ通常のインデックスが使われないのか

SQL例

SELECT first_name, last_name, phone_number
FROM employees
WHERE UPPER(last_name) = UPPER('winand');

問題点

  • last_name にインデックスがあっても使われない
  • 行計画は TABLE ACCESS FULL

理由(オプティマイザ視点)

  • データベースにとって
    last_nameUPPER(last_name)完全に別物
  • オプティマイザからは次のように見える
WHERE BLACKBOX(last_name) = 'WINAND'
  • UPPER() は「中身が分からない関数(ブラックボックス)」
  • 列の値と結果の対応関係が分からないため、インデックスが使えない

解決策:関数インデックスを使う

インデックス作成

CREATE INDEX emp_up_name
ON employees (UPPER(last_name));

効果

  • UPPER(last_name) をキーにした Bツリーインデックス
  • 実行計画は INDEX RANGE SCAN
  • フルテーブルスキャンを回避できる

実行計画の見積もりが変になる理由

見積もりの矛盾例

  • INDEX RANGE SCAN:40行取得と見積もり
  • TABLE ACCESS:100行取得と見積もり

これは 論理的にありえない
(インデックスで40行しか返さないのに、テーブルから100行読むことはできない)

原因:統計情報が古い

  • 関数インデックス作成時、Oracleは自動で統計情報を更新しない場合がある
  • 結果として、オプティマイザが誤った行数・コストを見積もる

対応

EXEC DBMS_STATS.GATHER_TABLE_STATS('HR', 'EMPLOYEES');
  • 統計情報更新後、行数・コスト見積もりは正常化
  • オプティマイザは正しい実行計画を選択できる

まとめ

  • UPPER() / LOWER() を WHERE句で使うと 通常インデックスは無効
  • 解決には 関数インデックス(Function-Based Index)
  • インデックス作成後は 必ず統計情報を更新
  • 「関数を使った検索」は
    インデックス設計 + 統計情報 の両方が揃って初めて高速になる
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?