AWRのSQL_IDに慣れていると、STATSPACKのHash Valueだけを頼りに実行計画へドリルダウンする手順には戸惑うはずです。重いSQLを特定してから実行計画にたどり着くまでの導線を、実際に何度もたどった手順そのままに書き起こしました。
SQL Statistics の全体像
図1: STATSPACKでのSQLチューニング対象特定
STATSPACKの SQL Statistics は、AWRと同様に複数の観点でSQLをランキングしています。 ただし識別子が Hash Value(Old Hash Value) である点と、ランキング種類が若干異なる点に注意が必要です。
| テーブル名 | ランキング基準 | 適した問題 |
|---|---|---|
| SQL ordered by CPU | 合計CPU消費時間 | CPU高負荷の原因SQL |
| SQL ordered by Elapsed Time | 合計経過時間 | 全体的に最も重いSQL(最重要) |
| SQL ordered by Gets | 論理読み込みブロック数 | バッファキャッシュを大量消費するSQL |
| SQL ordered by Reads | 物理読み込みブロック数 | I/Oを引き起こすSQL |
| SQL ordered by Executions | 実行回数 | 頻繁に呼ばれる軽いSQL(積み重ね) |
| SQL ordered by Parse Calls | パース回数 | バインド変数未使用SQL |
SQL Statisticsの各列定義は PART 05 — SQL Statistics 詳細(定義書) を参照。
Elapsed Time で重いSQLを特定する
まず SQL ordered by Elapsed Time を確認します。 合計Elapsed Timeが最も大きいSQLが全体負荷に最も寄与しています。
| 列名 | 意味 | 注目ポイント |
|---|---|---|
| Elapsed Time (s) | 合計経過時間(秒) | 最も総合的な重さを示す |
| Executions | 実行回数 | 高回数×軽い or 低回数×重いを区別 |
| Elapsed Time / Execute | 1回あたりの経過時間 | 1回が重いSQLを特定できる |
| % Total DB Time | DB Timeに占める割合 | 30%超のSQLは最優先でチューニング |
CPU / Gets / Reads から原因を分析する
| 状況 | 原因の仮説 | 次のアクション |
|---|---|---|
| CPU Time ≒ Elapsed Time | CPU使用が多い(ソート・ハッシュ結合) | 実行計画確認、インデックス追加検討 |
| Reads が多い | 物理I/O + バッファキャッシュミス | PART 06 I/O分析 へ |
| Gets が多い(Readsは少ない) | 論理読み込みが多い(効率の悪いアクセス) | 実行計画の見直し |
| Elapsed ≫ CPU | I/O待ちまたは競合待ち | PART 04 Wait分析 へ |
Hash Value から実行計画へのドリルダウン
STATSPACKではSQL識別子として Hash Value(Old Hash Value) が使われます。 AWRのSQL_IDとは異なる点に注意してください。
💡 Hash ValueでSQL全文と実行計画を確認する
STATSPACKのレポートでは、Hash ValueはSQLテキストへの参照番号です。
レポートの末尾の SQL Text セクションで対応するSQL全文を確認できます(Snap Level 5以上が必要)。
実行計画は PLAN_TABLE や V$SQL_PLAN で別途確認が必要です。
⚠️ Old Hash Value と New Hash Value
Oracle 10g以降では SQL_ID(新形式)と Hash Value(旧形式)が共存しています。
STATSPACKは従来の Old Hash Value を使います。AWRのSQL_IDと相互変換する場合は変換関数が必要です。
STATSPACKでのSQL特定ワークフロー
AWRと異なりOEMのGUIが使えないSTATSPACK環境では、レポート → Hash Value → SQL Textという手順での確認が基本になる。
| 手順 | 操作 | 確認ポイント |
|---|---|---|
| ① Elapsed Timeランキングで候補を絞る | spreport.sqlのSQL ordered by Elapsed Timeセクションを参照 | 上位5件に普段は現れないSQLがないか。Hash Valueをメモする |
| ② SQL Textセクションで全文を確認 | レポート末尾のSQL TextセクションでメモしたHash Valueを探す | Level 5以上のスナップでないと全文が出ない。STATSPACK.SNAPのi_snap_levelを確認 |
| ③ 複数スナップで傾向を比較する | 同じHash Valueを別スナップ間のレポートで確認 | Elapsed Timeが特定時間帯だけ急増していないか。バッチ時間帯と重なっていないか |
| ④ 実行計画をV$SQL_PLANで取得する | PERFSTAT.STATS$SQL_PLANかV$SQL_PLANをHash Valueで検索 | フルスキャン・大きなCardinality誤りがないか。AWRと異なり自動取得されないため手動確認が必要 |
優先順位付けの考え方
✅ SQLチューニングの優先順位
① % Total DB Time が高いSQL(全体負荷への寄与が大きい)
② Elapsed / Execute が異常に高いSQL(1回が重い)
③ Parse Calls ≒ Executions のSQL(毎回ハードパース)
④ 実行回数が多く積み重なっているSQL
SQL特定でよくある誤り
SQL Statistics のランキングは視点ごとに異なる。単一ランキングだけで判断すると重要なSQLを見落とす。
| 誤り | 何が起きるか | 正しい読み方 |
|---|---|---|
| 「Elapsed Time」ランキング1位のSQLだけをチューニング対象にする | 実行回数の多い低コストSQLの累積が全体に与える影響を見落とす | CPU / Reads / Executions の各ランキングも確認し、複数軸で上位に来るSQLを優先する |
| Parse Calls ≒ Executions のSQLを見落とす | 毎回ハードパースしているSQLが放置され、Shared Pool競合やCPU高騰の遠因になる | Executions に対して Parse Calls が高い割合のSQLを「バインド変数化」候補として抽出する |
| SQLテキストの省略(末尾の...)のまま最適化策を決める | WHERE句の全体像や結合条件が見えないまま対策を立てると、的外れな索引追加等が発生する | SQL_IDを特定したらV$SQLTEXTやDBMS_XPLAN.DISPLAY_CURSORで完全なSQLと実行計画を取得してから判断する |