1. はじめに:SQLチューニングは“読む力”と“意図を伝える力”
OracleのSQLチューニングは「書き方を変える」だけではありません。
データベースが**どう解釈して実行しているか(実行計画)を理解し、
必要に応じて意図的に誘導(HINT)**するのが本質です。

2. 実行計画を確認する基本:EXPLAIN PLANとAUTOTRACE
■ EXPLAIN PLAN の基本
1 2 3 4 5 |
EXPLAIN PLAN FOR SELECT * FROM SALES WHERE AREA = 'TOKYO'; SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY); |
実行結果の主なカラム:
カラム名 | 意味 |
---|---|
OPERATION | 実行操作(TABLE ACCESSなど) |
OPTIONS | 操作のタイプ(FULL, BY INDEX ROWIDなど) |
OBJECT_NAME | テーブル/インデックス名 |
COST | コスト見積もり値(低いほど速い傾向) |
■ AUTOTRACEを使って実行と同時に確認
1 2 3 4 |
SET AUTOTRACE ON; SELECT * FROM SALES WHERE AREA = 'TOKYO'; SET AUTOTRACE OFF; |
結果に実際のI/O統計と実行計画が表示されます。
👉 開発環境では常にON推奨。
3. インデックス設計の基本と誤解されやすい点
■ インデックスを張るべき列
ケース | 理由 |
---|---|
WHERE句で頻繁に使用 | 絞り込みに貢献 |
JOIN条件列 | 結合効率を改善 |
ORDER BY/GROUP BY列 | 並び替えコスト削減 |
■ よくある“悪い”例
1 2 |
WHERE TO_CHAR(order_date, 'YYYYMM') = '202510'; |
→ 関数が列にかかるとインデックスが無効化されます。
対策:関数索引(Function-Based Index)を使用。
1 2 |
CREATE INDEX idx_order_month ON orders(TO_CHAR(order_date, 'YYYYMM')); |
■ 複合インデックスの順序は“左から順”
1 2 |
CREATE INDEX idx_sales_area_date ON sales(area, order_date); |
→ WHERE句に area = 'TOKYO' AND order_date > SYSDATE - 30
があれば有効。
逆順条件(order_date
だけ)では使われないので注意。
4. 統計情報の更新でオプティマイザを賢くする
1 2 |
EXEC DBMS_STATS.GATHER_TABLE_STATS(ownname => 'SCOTT', tabname => 'SALES'); |
👉 統計情報が古いと実行計画が最適でなくなる。
定期ジョブで週1回更新が理想です。
5. 実践:フルスキャンを回避して速度を改善
■ 例:30万件の売上テーブルから東京の注文を抽出
1 2 |
SELECT * FROM SALES WHERE AREA = 'TOKYO'; |
実行計画:
1 2 |
TABLE ACCESS FULL (SALES) |
→ フルスキャン。
対応策:
1 2 |
CREATE INDEX idx_sales_area ON SALES(AREA); |
再実行:
1 2 3 |
TABLE ACCESS BY INDEX ROWID (SALES) INDEX RANGE SCAN (IDX_SALES_AREA) |
実行時間が 3.8秒 → 0.2秒 に短縮。
6. HINT句を使った実行計画の制御
Oracleのオプティマイザは非常に賢いですが、
時には意図通りの実行計画を選ばないこともあります。
そんなときに使えるのが HINT句 です。
■ 基本構文
1 2 3 4 |
SELECT /*+ INDEX(s idx_sales_area) */ * FROM SALES s WHERE AREA = 'TOKYO'; |
👉 /*+ ... */
内にヒントを記述。INDEX
は指定インデックスを強制使用します。
■ 主なHINT句一覧(現行でも使用可能)
HINT | 意味 | 用途 |
---|---|---|
FULL(table) | フルスキャンを強制 | 小テーブルなど |
INDEX(table index_name) | 指定インデックスを強制 | 逆にINDEXを使わせたい時 |
NO_INDEX(table) | インデックス使用禁止 | 誤判定を避けたい時 |
USE_NL | ネストループ結合を強制 | 小規模JOINに最適 |
USE_HASH | ハッシュ結合を強制 | 大規模JOINに有効 |
ORDERED | JOIN順序を指定 | オプティマイザ任せにしない |
PARALLEL | 並列実行を指定 | 大量データ抽出時 |
👉 2025年現在もHINT句は現役。
ただし、Oracle 19c以降では自動チューニング機能が賢くなっており、
乱用よりも「限定的な補助」に使うのがコツです。
7. 実務でのチューニング手順(例)
手順 | 作業 | コマンド例 |
---|---|---|
① 現状確認 | 実行計画を取得 | EXPLAIN PLAN |
② 絞り込み列確認 | WHERE条件を分析 | 条件列を洗い出す |
③ インデックス確認 | DBA_INDEXES を確認 | 既存索引を再利用 |
④ 統計情報更新 | DBMS_STATS | コスト計算を最新化 |
⑤ ヒント句適用 | 特殊ケースのみ | USE_NL など最終調整 |
8. 裏技:実行計画をキャッシュして差分確認する
1 2 3 |
SELECT * FROM V$SQL_PLAN WHERE SQL_ID = '2r6j3a7yq1n9fz'; |
👉 実際にOracleが選んだ実行計画を照会可能。
複数パターンを比較して「チューニング効果の見える化」ができます。
9. チューニング時の落とし穴まとめ
落とし穴 | 説明 | 回避策 |
---|---|---|
関数をWHERE句に使ってINDEX無効 | TO_CHAR(col) など | Function-Based Index |
統計情報が古い | 実行計画が誤る | DBMS_STATS 更新 |
OR条件が多い | フルスキャン化 | UNIONまたはINに分割 |
不要なORDER BY | ソートコスト増 | UI側で制御 |
LIKE ‘%xxx’ | インデックス無効 | INSTR活用 or N-gram索引 |
10. まとめ:オプティマイザを理解し、正しく導く
カテゴリ | 対策 | 効果 |
---|---|---|
インデックス設計 | 主キー+WHERE列優先 | 抽出効率化 |
統計更新 | DBMS_STATSで定期実施 | コスト最適化 |
実行計画分析 | DBMS_XPLAN/AUTOTRACE | ボトルネック特定 |
HINT活用 | INDEX, USE_NLなど | 意図的制御 |
SQLチューニングは**“DBに考えさせる”のではなく、“考える材料を与える”作業**です。
HINT句はそのための“囁き”のような存在。
適切に使えば、Oracleは想像以上に速く、正確に動きます。

コメント