「SQLが遅い、なんでだろう?」
そんなときに最初に確認するのが**実行計画(Execution Plan)**です。実行計画を読めるようになれば、SQLチューニングの半分は終わったようなものです。
この記事では、Oracleの実行計画を確認する3つの方法と、実行計画の読み方を現場SE歴20年超の筆者が解説します。
実行計画とは
実行計画とは、OracleがそSQLをどう実行するかのプランです。
- どのインデックスを使うか
- テーブルをどの順番で結合するか
- ソートやハッシュを使うか
Oracleのオプティマイザが統計情報をもとに「最も効率的な方法」を選びます。ただし統計情報が古かったり、オプティマイザの判断が誤っていると、非効率な実行計画が選ばれてSQLが遅くなります。
実行計画を確認する3つの方法
| 方法 | 特徴 | おすすめ場面 |
|---|---|---|
| EXPLAIN PLAN | SQLを実行せず計画だけ確認できる | 本番で実際に実行する前に確認したい |
| AUTOTRACE | SQL実行後に実行計画と統計を表示 | SQL*Plusで手軽に確認したい |
| DBMS_XPLAN.DISPLAY_CURSOR | 実際に実行された計画を確認(最も正確) | 正確な実行計画が必要なとき |
方法1:EXPLAIN PLANを使う
最もシンプルな方法です。SQLを実際には実行せず、実行計画だけを確認できます。
手順1:EXPLAIN PLANを実行
sql
EXPLAIN PLAN FOR
SELECT *
FROM EMP E
JOIN DEPT D ON E.DEPTNO = D.DEPTNO
WHERE E.SAL > 3000
;手順2:PLAN_TABLEから結果を表示
sql
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);出力例
Plan hash value: 1234567890
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 5 | 200 | 4 (0)| 00:00 |
| 1 | NESTED LOOPS | | 5 | 200 | 4 (0)| 00:00 |
| 2 | INDEX RANGE SCAN | IDX_SAL | 5 | 100 | 2 (0)| 00:00 |
| 3 | TABLE ACCESS FULL| DEPT | 4 | 80 | 2 (0)| 00:00 |
---------------------------------------------------------------------------方法2:AUTOTRACEを使う(SQL*Plus)
SQL*Plusで手軽に実行計画と実行統計を確認できます。
AUTOTRACEをONにする
sql
-- 実行計画と統計を両方表示
SET AUTOTRACE ON
-- 実行計画のみ表示(SQLは実行しない)
SET AUTOTRACE TRACEONLY EXPLAIN
-- 実行計画と統計を表示(SQLは実行するが結果は非表示)
SET AUTOTRACE TRACEONLY使用例
sql
SET AUTOTRACE ON
SELECT *
FROM EMP
WHERE DEPTNO = 10
;実行後、SQLの結果に続いて実行計画と実行統計が自動表示されます。
確認が終わったらOFFに
sql
SET AUTOTRACE OFF方法3:DBMS_XPLAN.DISPLAY_CURSORを使う(最も正確)
実際に実行された計画を確認する最も正確な方法です。EXPLAIN PLANは「予測」の計画ですが、DISPLAY_CURSORは「実際に使われた」計画を表示します。
手順1:対象SQLのSQL_IDを確認
sql
SELECT SQL_ID, SQL_TEXT
FROM V$SQL
WHERE SQL_TEXT LIKE '%FROM EMP%' -- 調べたいSQLのキーワードで絞る
AND SQL_TEXT NOT LIKE '%V$SQL%' -- このSQL自身を除外
;手順2:SQL_IDを使って実行計画を表示
sql
SELECT *
FROM TABLE(
DBMS_XPLAN.DISPLAY_CURSOR(
SQL_ID => 'ここにSQL_IDを入力'
,CURSOR_CHILD_NO => 0
,FORMAT => 'ALLSTATS LAST' -- 実際の行数・コストも表示
)
)
;
FORMAT => 'ALLSTATS LAST'について:ALLSTATS= 推定行数と実際の行数の両方を表示LAST= 直近の実行のデータを使用
実行計画の読み方
基本的な読み方:内側(インデント深い)から上へ
実行計画はインデントが深いほど先に実行されます。
| 0 | SELECT STATEMENT | ← 最後
| 1 | HASH JOIN | ← 3番目
| 2 | TABLE ACCESS FULL | EMP ← 1番目
| 3 | TABLE ACCESS FULL | DEPT ← 2番目主要なOperation(操作)の意味
| Operation | 意味 | 良い/悪い |
|---|---|---|
| TABLE ACCESS FULL | フルスキャン | 件数が多いと⚠️ |
| INDEX RANGE SCAN | インデックス範囲スキャン | 通常は✅ |
| INDEX UNIQUE SCAN | ユニークインデックス | ✅ |
| NESTED LOOPS | ネステッドループ結合 | 外部表が小さいなら✅ |
| HASH JOIN | ハッシュ結合 | 大量データなら✅ |
| SORT (ORDER BY) | ソート処理 | メモリに注意 |
RowsとEstimated/Actual Rowsの乖離に注目
| Id | Operation | Rows(推定) | A-Rows(実際) |
|----|-----------------|-----------|-------------|
| 1 | TABLE ACCESS.. | 5 | 50000 |推定5行に対して実際50,000行 → 統計情報が古い可能性が高い。統計情報の収集が必要です。
よくある遅いパターンと対策
パターン1:不要なTABLE ACCESS FULL
症状: 件数の多いテーブルでFULL SCANが出ている
対策:
- WHERE句の列にインデックスを作成
- 統計情報を最新化してインデックスが使われるようにする
パターン2:推定行数と実際の行数が大きく乖離している
症状: Rows=5なのにA-Rows=100,000
対策:
DBMS_STATS.GATHER_TABLE_STATSで統計を収集する- ヒストグラムを取得する(列の値の偏りがある場合)
よくある質問(FAQ)
Q. EXPLAIN PLANとDISPLAY_CURSORの結果が違うのはなぜですか?
EXPLAIN PLANは統計情報にもとづく「予測」の実行計画です。DISPLAY_CURSORは実際に実行された計画のため、バインド変数の値によって異なる計画が使われることがあります。より正確な調査にはDISPLAY_CURSORを使ってください。
Q. 実行計画を変えずにSQLをチューニングするには?
ヒント句(/*+ INDEX(table_name index_name) */ など)を使うと、オプティマイザに実行計画を指示できます。ただしヒント句に頼りすぎると保守性が下がるため、根本原因(統計情報・インデックス設計)の見直しを推奨します。
Q. EXPLAIN PLANを使うにはどんな権限が必要ですか?
EXPLAIN PLAN 文の実行には EXPLAIN ANY または対象テーブルへのSELECT権限が必要です。PLAN_TABLE は通常パブリックシノニムとして全ユーザーが使用できます。
まとめ
| 方法 | コマンド | 特徴 |
|---|---|---|
| EXPLAIN PLAN | EXPLAIN PLAN FOR SQL; → SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY); | SQLを実行せずに確認 |
| AUTOTRACE | SET AUTOTRACE ON; | SQL*Plusで手軽に確認 |
| DISPLAY_CURSOR | DBMS_XPLAN.DISPLAY_CURSOR(SQL_ID) | 最も正確・実際の実行計画 |
SQLが遅い原因は実行計画を見れば8割わかります。まずは EXPLAIN PLAN で確認する習慣をつけ、より詳しく調べたいときは DISPLAY_CURSOR を使いましょう。





コメント