0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

SQLチューニングに必要な知識

私が初めてSQLパフォーマンス改善に取り組む際、「どこから手を付ければいいのか?」状態でした。

  • 何が原因で遅いのか?
  • どこを改善すべきなのか?
  • そもそも改善の方向性があっているのか?

といった判断ができずに、試行錯誤の末にとりあえずwhere句で使用しているカラムにインデックスを貼るくらいしか対策が思いつきませんでした。

そこでこの記事は自身の備忘録として、チューニングを行う前に抑えていきたい知識を体系化してまとめてみることにしました。

:pushpin:この記事で扱う内容

STEP1 SQLの遅くなる理由の本質理解  本記事
STEP2 実行計画の見方 次回記事
STEP3 効果的なインデックスの貼り方 次回記事


STEP1. SQLが遅くなる理由の本質理解

SQLが遅くなる原因は複雑に見えますが、本質的には次の4つのどれかです。

1️⃣ 必要以上に多くのデータを読み込んでいる
2️⃣ 読み込んだデータを効率よく絞り込めていない
3️⃣ データの並び替え・集約する処理が重い
4️⃣ 結合方法が適切でない

多くの遅いSQLはこのいずれかに該当しています。
チューニングとはその原因を特定し、必要な最小処理で目的を達成するように調整する作業です。

以下の具体例付きで解説します。


1️⃣ 必要以上に多くのデータを読み込んでいる
:scream: 悪い例

SELECT *
FROM USERS;

→ 必要な列ではなく SELECT * を使ってしまい、テーブルの全件・全カラムを読み込む。
索引も効かず、フルスキャンになりやすい。
:blush: 改善例

SELECT USER_ID, USER_NAME
FROM USERS
WHERE STATUS = 1;

→ 必要なカラムだけ取得し、条件で絞ることで読み込み量を最小化。


2️⃣ 読み込んだデータを効率よく絞り込めていない
:scream: 悪い例

WHERE TO_CHAR(CREATE_DATE, 'YYYY-MM-DD') = '2024-12-01'

→ TO_CHAR() による加工がWHERE句にあり、インデックスが効かないためテーブル全体を走査。

:blush: 改善例

WHERE CREATE_DATE >= DATE '2024-12-01'
AND   CREATE_DATE <  DATE '2024-12-02'

→ 加工せず比較することで、CREATE_DATEインデックスが貼ってある場合利用可能。


3️⃣データの並び替え・集約処理が重い
:scream: 悪い例

ORDER BY CREATE_DATE DESC;

→ 大量データに対する ORDER BY はソート処理が発生し高コスト。
必要以上の並び替えはパフォーマンスを悪化させる。

:blush: 改善例

  • 事前に索引付きの列で絞る
  • 必要な件数だけ取得(例:TOP N)
SELECT *
FROM (
    SELECT * FROM USERS ORDER BY CREATE_DATE DESC
)
WHERE ROWNUM <= 50;


4️⃣結合方法が適切でない

:scream: 悪い例

SELECT *
FROM ORDERS O
JOIN USERS U ON TO_CHAR(O.USER_ID) = U.USER_ID;

→ 結合条件に加工関数が含まれると、結合先のインデックスが無効化され、ネストループやハッシュ結合が余計に走る。

:blush: 改善例

  • 事前に索引付きの列で絞る
  • 必要な件数だけ取得(例: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 効果的なインデックスの貼り方 


参考資料

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?