Elapsed Timeで上位のSQLとCPU Timeで上位のSQLが一致せず、どちらを優先すべきか判断がつかなかったことがあります。SQL_IDの追い方を一から見直したときの視点をここに残します。
SQL Statisticsセクションには複数のランキングが並びます。それぞれが異なる観点でSQLをランク付けしているため、状況に応じて見るべきランキングが変わります。
Top 5 Timed Events で問題の方向性(CPU過多・I/O過多・ロック競合など)を把握した後、そのボトルネックに対応するランキングを見ることが重要です。やみくもに全ランキングを読むのではなく、仮説を持ってここに来るのが効率的です。
Elapsed Time Top 10
図1: SQL StatisticsセクションのTop SQL特定フロー
総実行時間が長いSQL。長時間実行されている処理を特定するときに見ます。
代表的なパターン
SELECT * FROM 大きなテーブル ...- 複雑な結合クエリ
- バッチ系の重いSQL
Elapsed Time Top 10 では、同じSQLでも実行回数(EXECUTIONS)と1回あたりの実行時間(Elapsed Time per Exec)を合わせて確認することが重要です。「1回あたりは速いが頻繁に実行されるSQL」と「1回あたりが遅いSQL」では対処法が異なります。前者はキャッシュや実行制御、後者は実行計画の改善が有効です。
着目ポイント: SQL_ID をコピーして実行計画(EXPLAIN PLAN)を確認する。
CPU Time Top 10
CPU消費量が多いSQL。解析・ソートが多い処理を特定するときに見ます。
代表的なパターン
GROUP BY/ORDER BY- 複雑な計算ロジック
- 関数を多用したWHERE句
CPU Time が高いSQLのもう一つの原因がハードパースです。バインド変数を使わずにリテラル値(例:WHERE id = 123)を直接埋め込んだSQLは、実行のたびに異なるSQL文として扱われ、その都度新しい実行計画を生成するため、CPUを消費します。CPU Time Top 10 に似た形のSQLが複数登場している場合はバインド変数化を検討してください。
着目ポイント: ハードパース・解析過多の場合はバインド変数の使用を検討する。
Physical Reads Top 10
物理ディスク読み込みが多いSQL。インデックス未使用・フルスキャンを特定するときに見ます。
代表的なパターン
- Full Table Scan クエリ
- インデックス未使用クエリ
- 巨大データのスキャン
Physical Reads が多い場合、Buffer Cache にデータがキャッシュされておらず、ディスクから読み直しているケースが大半です。インデックスを追加するか、クエリの条件を絞り込むことで Physical Reads を削減できる場合があります。一方で、バッチ処理など「大量のデータを一度だけ読む」ケースでは Physical Reads が多くなることは正常であり、むしろ Buffer Cache を温存するために RESULT_CACHE や並列読み取りを使うアプローチが有効です。
着目ポイント:EXECUTIONS(実行回数)も合わせて確認。
少ない実行回数で読み込み量が多い = 危険な兆候。
その他のランキング
SQL Statistics には上記3つ以外にも次のランキングがあります。状況に応じて参照してください。
| ランキング | 着目する状況 |
|---|---|
| Gets (Buffer Gets) Top 10 | 論理読み込みが多い = キャッシュは効いているが読み込み量が多すぎる |
| Executions Top 10 | 非常に頻繁に実行されるSQLの特定。小さくても積み重なって負荷になる |
| Parse Calls Top 10 | ハードパース多発の特定。バインド変数化の優先度が高いSQLを見つける |
| Sharable Memory Top 10 | Shared Pool を大量消費するSQL。Shared Poolが枯渇気味な場合に確認 |
SQL_IDの活用
各ランキングには SQL_ID(Oracle がSQLを一意に識別する文字列)が付随しています。これをコピーして以下のような調査に活用します。
V$SQL/DBA_HIST_SQLTEXTでSQL本文を確認DBMS_XPLANで実行計画を確認- SQLチューニングアドバイザに投入
AWRレポートをHTML形式で生成している場合、SQL_IDはレポート内のリンクになっており、クリックでSQL本文や実行計画にジャンプできます。
SQL本文の確認は次のクエリで行えます。
-- SQL_IDからSQL本文を取得(実行中のSQLから)
SELECT sql_id, sql_text
FROM v$sql
WHERE sql_id = '&sql_id';
-- AWR履歴(過去のSQL)から取得
SELECT sql_id, sql_text
FROM dba_hist_sqltext
WHERE sql_id = '&sql_id';
実行計画を確認するときは DBMS_XPLAN.DISPLAY_AWR を使うと、AWRに記録された過去の実行計画を参照できます。現在と過去の実行計画を比較することで、性能劣化が実行計画の変化によるものかどうかを判断できます。
-- AWRに保存された実行計画を表示
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_AWR('&sql_id'));
SQL Statistics読み方でよくある誤り
| 誤り | 何が起きるか | 正しい対処 |
|---|---|---|
| Elapsed Time Top 10だけ見て対象SQLを決める | 実行回数が非常に多い低コストSQLが積み重なって全体負荷になっているケースを見落とす | Executions Top 10もあわせて確認し、「頻度 × 単体コスト」でトータル負荷を評価する |
| SQLチューニング後にAWRで再確認しない | チューニング済みSQLのSQL_IDが変わり、改善効果が正しく測れないことがある | チューニング前後のAWRレポートを比較(awrddrpt.sql)してSQL別の変化を数値確認する |
| Parse Calls Top 10を見落とす | バインド変数未使用でハードパースが連続するSQLはElapsed時間が短くても共有プールを圧迫する | Parse Calls が多いSQLはバインド変数化を最優先に検討する |