はじめに
これまで私は、SQLを書くとき「正しくデータが取れるか」ばかりを気にしていました。ところがある日、テスト項目に「SQLのパフォーマンスを計測して確認」という一文が登場。頭の中に「???」が一気に増殖しました。 (計測…? 確認…? SELECT
が通って必要なデータが返れば十分じゃないの?)
そこで「SQLのパフォーマンスをどう“確認”するのか」を調べ始めたところ、どの記事にも必ず現れるキーワードがありました——実行計画。読み進めるうちに、「この実行計画を理解し、読み解けるようになることが、SQLの性能を客観的に評価する第一歩だ」と腑に落ちました。
本メモは、その気づきから私が学んだことを自分用の備忘録としてまとめるものです。
「正しく動く」から一歩進んで、「速く・無駄なく・再現性高く動く」を目指すために——まずは実行計画とは何か、そしてどのように見るべきかを整理しておきます。
1. 実行計画とは?~SQLの「処理手順書」~
Oracle実行計画(Execution Plan)とは、OracleデータベースがSQL文を実行する際に採用する処理手順(アクセスパス)を示した情報です。 この実行計画を見ることで、SQLの内部的な動きを具体的に知ることができます。
-
処理の順序: どのテーブルから先にアクセスするか
-
アクセス方法: テーブルのデータを全件スキャン(
TABLE ACCESS FULL
)するのか、インデックスを使って効率的にアクセス(INDEX SCAN
)するのか -
結合方式: 複数のテーブルをどうやって結合するか(
NESTED LOOPS
,HASH JOIN
など)
この「処理手順」が非効率だと、SQLのパフォーマンスは著しく低下します。そのため、SQLが遅いと感じた時、まず実行計画を確認することが問題解決の第一歩となります。
2. 実行計画がパフォーマンス改善の"羅針盤"である3つの理由
理由1: オプティマイザによる"見積もりコスト"がわかるから
実行計画には、オプティマイザが計算した処理ごとの**コスト(Cost)**が数値で示されます。 このコストを見れば、どの処理が重いと判断されているのかが一目瞭然です。特にTABLE ACCESS FULL
(全件スキャン)のように、明らかにコストが高くなりがちな処理が動いていないかを確認できます。
※オプティマイザ(Optimizer)とはデータベースや検索エンジンにおいて、与えられた条件に対して最も効率的に処理できる方法を選ぶ仕組みのことを指します。 特にデータベースでは SQL文をどの順番で処理するか、どのインデックスを使うか を判断し、実行計画を作成する役割を担っています。
理由2: インデックスが "なぜ" 使われていないのか原因を探れるから
「このインデックスを貼ったのに使われない!」というケースはよくあります。その原因は様々です。
※インデックスとは、テーブルから特定のデータを高速に検索するための「索引」のようなものです。
-
SQLの書き方(例:
WHERE
句で列に関数を使っている)が原因で、インデックスが使えない。 -
統計情報が古く、オプティマイザが「インデックスを使うより全件スキャンした方が速い」と誤った判断をしてしまっている。
実行計画を見れば、インデックスが使われているか(INDEX SCAN
)いないか(FULL SCAN
)がすぐにわかり、原因調査の大きな手がかりになります。
理由3: "感覚"ではなく"根拠"に基づいた改善サイクルを回せるから
実行計画を使えば、パフォーマンス改善を「勘」に頼らず、論理的に進めることができます。
-
As-Is(現状把握): まずは現状のSQLの実行計画を取得する。
-
仮説立案: 実行計画のコストやアクセス方法を見て、「このカラムにインデックスがあれば速くなりそう」といった仮説を立てる。
-
改善実施: 実際にインデックスを作成するなどの改善を行う。
-
To-Be(効果測定): 再度、実行計画を取得し、改善前と比較する。
このサイクルを回し、Cost
が下がったり、アクセスパスがTABLE ACCESS FULL
からINDEX SCAN
に変わったりしたことを確認することで、施策の効果を客観的に判断できます。
3. Oracleでの実行計画の確認方法(基本のキ)
簡単な例で見ていきましょう。
サンプルSQL
ここでは、以下のような社員テーブル EMPLOYEES
があるとします。
-
EMP_ID
(社員ID) -
EMP_NAME
(社員名) -
DEPT_ID
(部署ID)
このテーブルから、特定の部署に所属する社員を探す、以下のSQLの実行計画を見てみます。
EXPLAIN PLAN FOR
SELECT * FROM EMPLOYEES WHERE DEPT_ID = 10;
実行計画の表示
上のコマンドを実行した後、以下のSQLで計画の詳細を表示します。
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);
実行計画の出力例(インデックスなし)
例えば以下のような実行計画が表示されます。(※環境により表示は異なります)
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 346759934
--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 15 | 390 | 3 (0)| 00:00:01 |
|* 1 | TABLE ACCESS FULL| EMPLOYEES | 15 | 390 | 3 (0)| 00:00:01 |
--------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("DEPT_ID"=10)
出力例のポイント
Operation
の列に注目してください。TABLE ACCESS FULL
となっています。これはEMPLOYEES
テーブルの全データを1行ずつスキャンして、「DEPT_ID = 10
」の条件に合うか調べていることを意味します。今はデータが少ないので問題ありませんが、テーブルの行数が数百万、数千万になると、この処理は非常に遅くなる場合があります。
では、このパフォーマンスを改善するにはどうすればよいでしょうか。
WHERE
句で条件を指定しているDEPT_ID
列にインデックスを作成してみます。
-- DEPT_ID列にインデックスを作成
CREATE INDEX idx_employees_dept_id ON EMPLOYEES(DEPT_ID);
インデックス作成後、再度同じSQLの実行計画を確認すると、次のように変化します。
実行計画の出力例(インデックスあり)
PLAN_TABLE_OUTPUT
---------------------------------------------------------------------------------------------------
Plan hash value: 123456789
---------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 15 | 390 | 1 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| EMPLOYEES | 15 | 390 | 1 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | IDX_EMPLOYEES_DEPT_ID | 15 | | 1 (0)| 00:00:01 |
---------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("DEPT_ID"=10)
Operation
が TABLE ACCESS FULL
から INDEX RANGE SCAN
に変わりました。これは、作成したインデックスを使ってDEPT_ID
が10
のデータを探し、見つかった行にだけアクセスする方法に変わったことを示します。これにより、テーブルの全件をスキャンする必要がなくなり、検索効率が大幅に向上します。
このように、実行計画はパフォーマンスのボトルネックを特定し、改善策の効果を測定するための重要な手がかりとなります。
まとめ
これからは「とりあえず実行計画」の意味を理解して、自信を持って確認できそうです。
POINT
- SQLが遅い時、「実行計画」を確認するのは、そのSQLが非効率な動きをしていないかを知るため!
- 実行計画は、パフォーマンス問題の原因を特定するための、超強力な手がかり✨
採用拡大中!
アシストエンジニアリングでは一緒に働くフロントエンド、バックエンドのエンジニア仲間を大募集しています!
少しでも興味ある方は、カジュアル面談からでもぜひお気軽にお話ししましょう!
お問い合わせはこちらから↓
https://official.assisteng.co.jp/contact/