5
4

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チューニングテクニック 〜構造を変えずにヒント句で高速化〜

Posted at

はじめに

「SQLが遅いな…」と感じたこと、ありませんか?
僕も入社して最初の案件で、SQLの実行が遅くてレビューで何度も指摘されました。

SQLチューニングというと「インデックスを貼ろう」「正規化しよう」といった話になりがちですが、現場ではテーブル構造を変えられない制約も多いです。

そこで今回は、SQLの構造を変えずにできるチューニング方法を紹介します。
特に「ヒント句」という、ちょっと便利な裏技的テクニックに注目していきます。


1. チューニングの基本ステップ

まずは、「SQLが遅い!」となったときに僕が学んだ基本ステップを共有します。

  1. 遅いSQLを特定する(APMやスロークエリログなど)
  2. 実行計画を確認する
  3. ボトルネックを見つける
  4. 対応策を検討する(構造変更が無理ならヒント句を検討)

この記事では特に「SQLやテーブルを変更できない」ケースに役立つ対策を中心に紹介します。


2. 実行計画を読む(最低限でOK)

実行計画と聞くと難しそうですが、最低限の知識でもチューニングには十分役立ちます。

例:PostgreSQL の EXPLAIN

EXPLAIN SELECT * FROM users WHERE age = 30;
Seq Scan on users  (cost=0.00..431.00 rows=5000 width=64)
  Filter: (age = 30)

この出力から「インデックス効いてないな」といったことがわかります。

実行計画に出てくる各値の意味

PostgreSQLの実行計画には、以下のような数値情報も含まれています:

項目 意味
cost クエリプランナーが予測したコスト(開始コスト..終了コスト)で、相対的な重さを表します。単位はPostgreSQL内部独自で、実行時間とは異なります。
rows プランナーが予測する出力行数。実際の実行時とは異なることがあります。
width 出力される1行あたりのバイト数の推定値。

これらを参考にして、SQLが「どこで重くなっているのか」を見極めることができます。

よく出る用語と意味(ノード種別)

用語 意味
Seq Scan テーブル全件をスキャン(遅い)
Index Scan インデックスを利用した検索(速い)
Nested Loop 小さなデータには強いループ結合方式
Hash Join 大量データの結合に強い方式

3. 遅いSQLのよくあるパターンと対策(+ヒント句例)

ここでは、実行計画に現れやすい典型的な「遅いSQL」のパターンと、その原因、ヒント句による具体的な対処方法をまとめます。

✅ ポイント:問題の「傾向」と「それに対して強制すべき実行方法(ヒント句)」を対応させるのが効果的です。

アンチパターン 実行計画の兆候 問題の本質 ヒント句で強制する内容
全件スキャン Seq Scan, type: ALL インデックスが使われていない スキャン方式(IndexScan)
重いNested Loop Nested Loop + 大量データ 結合順・結合方式が非効率 結合方式(HashJoinやMergeJoin)
後段のFilter Filter: が遅延評価される 条件が早期に使われていない アクセスパスと結合順(IndexScan, Leading)
SortやGroup Byが遅い Sort, HashAggregate が高コスト 並び替えや集計がインデックス非活用 スキャン順序(IndexScan with ORDERED)

以下で、それぞれの例とヒント句の使い方を詳しく紹介します。

パターン1:全件スキャン

  • 原因:インデックスが使われていない
  • 実行計画に Seq Scantype: ALL が出てくる

ヒント句で改善:

-- Oracle
SELECT /*+ INDEX(users users_age_idx) */ * FROM users WHERE age = 30;
-- MySQL
SELECT * FROM users USE INDEX (users_age_idx) WHERE age = 30;
-- PostgreSQL(pg_hint_plan)
SELECT /*+ IndexScan(users) */ * FROM users WHERE age = 30;

パターン2:大量データ × Nested Loop

  • 原因:結合対象が大きく、インデックスも使われていない

ヒント句で改善:

-- Oracle
SELECT /*+ USE_NL(a b) INDEX(b b_fk_idx) */ * FROM a JOIN b ON a.id = b.a_id;
-- MySQL
SELECT STRAIGHT_JOIN * FROM a JOIN b USE INDEX (b_fk_idx) ON a.id = b.a_id;
-- PostgreSQL(pg_hint_plan)
SELECT /*+ NestLoop(a b) Leading(a b) */ * FROM a JOIN b ON a.id = b.a_id;

パターン3:Filterで後から絞る

  • 原因:WHEREやJOINの条件がインデックスに効いていない

ヒント句で改善:

-- Oracle
SELECT /*+ INDEX(users users_age_status_idx) */ * FROM users WHERE age = 30 AND status = 'active';
-- MySQL
SELECT * FROM users USE INDEX (users_age_status_idx) WHERE age = 30 AND status = 'active';
-- PostgreSQL(pg_hint_plan)
SELECT /*+ IndexScan(users) */ * FROM users WHERE age = 30 AND status = 'active';

パターン4:Sort や GROUP BY に時間がかかる

  • 原因:該当列にインデックスがない、または非効率な集計

ヒント句で改善:

-- Oracle
SELECT /*+ INDEX_ASC(users users_created_at_idx) */ * FROM users ORDER BY created_at;
-- MySQL
SELECT * FROM users USE INDEX (users_created_at_idx) ORDER BY created_at;
-- PostgreSQL(pg_hint_plan)
SELECT /*+ IndexScan(users) Leading(users) */ * FROM users ORDER BY created_at;

4. 各DBでのヒント句の書き方

Oracle

SELECT /*+ INDEX(table index_name) */ ...

MySQL

SELECT * FROM table USE INDEX (index_name) ...

PostgreSQL

PostgreSQLは標準でヒント句がありませんが、セッション変数である程度制御できます。

SET enable_seqscan TO off;

さらに、拡張機能 pg_hint_plan を導入すればヒント句が使えます:

SELECT /*+ IndexScan(table) */ ...

※ 詳しくは pg_hint_plan GitHub を参照


5. ヒント句の注意点

  • DBごとに文法が異なる
  • 将来のバージョンで非推奨になる可能性もある
  • 不適切な指定で逆に遅くなるケースもある

ヒント句は「最終手段」として慎重に使うのがベストです。


6. 実際にどう変わった?(事例)

以下、実際の改善事例(PostgreSQL)です:

-- ヒント句なし
EXPLAIN ANALYZE SELECT * FROM users WHERE age = 30;
-- 実行計画: Seq Scan on users (cost=0.00..431.00 rows=5000)
-- ヒント句あり(pg_hint_plan)
EXPLAIN ANALYZE SELECT /*+ IndexScan(users) */ * FROM users WHERE age = 30;
-- 実行計画: Index Scan using users_age_idx on users

結果:実行時間が大幅に改善(例:800ms → 60ms など)


まとめ

  • SQLが遅いときはまず実行計画を確認
  • パターンを把握して原因を探る
  • 構造変更できないならヒント句で対策
  • ヒント句は使い方次第。過信せず慎重に!

僕自身、何度もヒント句に救われてきました。
「構造はいじれないけど、なんとかしたい…」という場面では、きっと役立つはずです。


参考リンク


おわりに

最後まで読んでいただきありがとうございました!
この記事は、僕が新人時代に困った経験をもとにまとめました。

「こういうケースも知りたい」「もっと実例が見たい」といったリクエストがあれば、ぜひコメントください!

5
4
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
5
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?