SQLチューニングに必要な知識
私が初めてSQLパフォーマンス改善に取り組む際、「どこから手を付ければいいのか?」状態でした。
- 何が原因で遅いのか?
- どこを改善すべきなのか?
- そもそも改善の方向性があっているのか?
といった判断ができずに、試行錯誤の末にとりあえずwhere句で使用しているカラムにインデックスを貼るくらいしか対策が思いつきませんでした。
そこでこの記事は自身の備忘録として、チューニングを行う前に抑えていきたい知識を体系化してまとめてみることにしました。
この記事で扱う内容
STEP1 SQLの遅くなる理由の本質理解 本記事
STEP2 実行計画の見方 次回記事
STEP3 効果的なインデックスの貼り方 次回記事
STEP1. SQLが遅くなる理由の本質理解
SQLが遅くなる原因は複雑に見えますが、本質的には次の4つのどれかです。
1️⃣ 必要以上に多くのデータを読み込んでいる
2️⃣ 読み込んだデータを効率よく絞り込めていない
3️⃣ データの並び替え・集約する処理が重い
4️⃣ 結合方法が適切でない
多くの遅いSQLはこのいずれかに該当しています。
チューニングとはその原因を特定し、必要な最小処理で目的を達成するように調整する作業です。
以下の具体例付きで解説します。
1️⃣ 必要以上に多くのデータを読み込んでいる
悪い例
SELECT *
FROM USERS;
→ 必要な列ではなく SELECT * を使ってしまい、テーブルの全件・全カラムを読み込む。
索引も効かず、フルスキャンになりやすい。
改善例
SELECT USER_ID, USER_NAME
FROM USERS
WHERE STATUS = 1;
→ 必要なカラムだけ取得し、条件で絞ることで読み込み量を最小化。
2️⃣ 読み込んだデータを効率よく絞り込めていない
悪い例
WHERE TO_CHAR(CREATE_DATE, 'YYYY-MM-DD') = '2024-12-01'
→ TO_CHAR() による加工がWHERE句にあり、インデックスが効かないためテーブル全体を走査。
改善例
WHERE CREATE_DATE >= DATE '2024-12-01'
AND CREATE_DATE < DATE '2024-12-02'
→ 加工せず比較することで、CREATE_DATEインデックスが貼ってある場合利用可能。
3️⃣データの並び替え・集約処理が重い
悪い例
ORDER BY CREATE_DATE DESC;
→ 大量データに対する ORDER BY はソート処理が発生し高コスト。
必要以上の並び替えはパフォーマンスを悪化させる。
改善例
- 事前に索引付きの列で絞る
- 必要な件数だけ取得(例:TOP N)
SELECT *
FROM (
SELECT * FROM USERS ORDER BY CREATE_DATE DESC
)
WHERE ROWNUM <= 50;
4️⃣結合方法が適切でない
悪い例
SELECT *
FROM ORDERS O
JOIN USERS U ON TO_CHAR(O.USER_ID) = U.USER_ID;
→ 結合条件に加工関数が含まれると、結合先のインデックスが無効化され、ネストループやハッシュ結合が余計に走る。
改善例
- 事前に索引付きの列で絞る
- 必要な件数だけ取得(例:TOP N)
SELECT *
FROM ORDERS O
JOIN USERS U ON O.USER_ID = U.USER_ID;
→ 結合キーは加工せず型を揃えておくことが大切。
なぜ同じ結果のSQLでも速度が変わるのか?
SQLは宣言型言語(非手続き型言語)だからです。
宣言的な言語とは…
どうやってやるかじゃなくて何をやりたいかを書くようなプログラミング言語。
どうやって目的を達成するかはコンパイラが勝手に決めます。
そのため、以下のように見た目がほぼ同じSQLでも、実行性能は全く異なります👇
-- インデックスが使えない例
WHERE TO_CHAR(REQUEST_DATE, 'YYYY-MM-DD') = '2025-01-01'
-- インデックスが使える例
WHERE REQUEST_DATE >= DATE '2025-01-01'
AND REQUEST_DATE < DATE '2025-01-02'
結果は同じでも、DBが採用するアクセス経路(Access Path)が異なるためです。
SQLの速度を左右する最大要素:アクセスパス
SQLが実際にデータへどのようにアクセスしているかを示すのがアクセスパスです。
代表的なものは以下です👇
| アクセス方式 | 性能 | 用途 |
|---|---|---|
| Full Table Scan | ❌ 遅い(大規模データで顕著) | 適切なインデックスがない場合/全件処理 |
| Index Range Scan | ⭕ 速い | WHERE条件で絞り込み時 |
| Index Full Scan | △ | インデックスだけで完結できる時 |
| Full Index Scan | △〜❌ | 複合インデックス順利用不可時 |
遅いSQLの多くは、本来Index Range Scanが期待される状況で、Full Table Scanになっていることが原因です。
この記事の結論
SQLが遅いのは、必要ないデータまで読み込み、加工しながら探そうとするから。
最初に必要なデータだけ拾えるように書く。
この考え方が、チューニング全体の軸になります。
次回記事で以下を執筆予定です。
STEP2 実行計画の見方
STEP3 効果的なインデックスの貼り方
参考資料