先のエントリの説明で、実行計画をいわば「形式的には読める(*1)」ようにはなりました。
しかし、実行計画が何をやっているのかという、意味的な観点では「理解できていない」はずです。
実行計画の処理内容を理解するためには、実行計画を構成するオペレーションの処理内容を理解する
必要があります。
* (*1) 「たどれる」という表現のほうが近いかもしれませんが・・・
### オペレーションの一覧
Oracle Databaseには非常に多くのオペレーションが存在し、
大部分のオペレーションについては、マニュアルの以下の箇所で説明されています。
* [10.2](http://otndnld.oracle.co.jp/document/products/oracle10g/102/doc_cd/server.102/B19207-02/ex_plan.htm#59155)
* [11.1](http://otndnld.oracle.co.jp/document/products/oracle11g/111/doc_dvd/server.111/E05743-02/ex_plan.htm#sthref1154)
* [11.2](http://download.oracle.com/docs/cd/E16338_01/server.112/b56312/ex_plan.htm#i23461)
### 最低限覚えておくべきオペレーション
全てのオペレーションに関して説明することは、現実的でないため、
先のエントリで説明した実行計画に含まれるオペレーションを説明します。
なお、このエントリに含まれるオペレーションは、非常に多くの実行計画で使用されうる、
非常に重要なものであり、最低限覚えておく必要があります。
—————————————————————————————–
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
—————————————————————————————–
| 0 | SELECT STATEMENT | | | | 20 (100)| |
| 1 | NESTED LOOPS | | 10 | 40120 | 20 (0)| 00:00:01 |
|* 2 | TABLE ACCESS FULL | PA | 1 | 2004 | 9 (0)| 00:00:01 |
| 3 | TABLE ACCESS BY INDEX ROWID| CH | 10 | 20080 | 11 (0)| 00:00:01 |
|* 4 | INDEX RANGE SCAN | IDX_CHPA | 10 | | 1 (0)| 00:00:01 |
—————————————————————————————–
なお、上記の実行計画は
SELECT /*+ FULL(PA) INDEX(CH idx_chpa) USE_NL(CH) LEADING(PA) */
cid, cname, pa.pid, pname
FROM ch, pa WHERE ch.pid = pa.pid and pa.pid = 1
を実行したときのものです。(*2)
* (*2) 今回の説明の本筋ではないので気にしていただく必要はないのですが、説明に適した実行計画に誘導するために、Multi Join Key Pre-fetchingを無効化しています。
### 実行イメージ
以下の文章では逐次オペレーションについて説明しますが、この図を参考に読んでいただくと理解しやすいはずです。
### TABLE ACCESS (FULL)
実際の実行計画の実行順序に即して説明してゆきます。
この実行計画では、最初にId=2 TABLE ACCESS FULLが実行されます。
|* 2 | TABLE ACCESS FULL | PA | 1 | 2004 | 9 (0)| 00:00:01 |
TABLE ACCESS FULLは指定された表の全体をスキャン(フルスキャン)して、
表に格納された行データを取得するオペレーションです。
仕組みからわかるとおり、取得したい行の数が、表全体の行数に比べて多い場合には
効率的なデータアクセス方法ですが、少ない場合は非効率的です。
この実行計画では、PA表から1行のデータを得たとします。
### INDEX RANGE SCAN
この実行計画では、次にId=4 INDEX RANGE SCAN が実行されます。
|* 4 | INDEX RANGE SCAN | IDX_CHPA | 10 | | 1 (0)| 00:00:01 |
Id=2 TABLE ACCESS FULL で得た pa.pid=1にマッチしたPA表の行に該当するデータを
CH表から取得するために実行しています。
索引をスキャンし、検索条件にマッチする索引列を含む行の識別子(ROWID)を得ます。
今回の実行計画では、索引列は一意ではないとしています。
この場合は、索引をスキャンするオペレーションの名称はINDEX RANGE SCAN であり、
複数行の識別子(複数のROWID)が返される場合があります。
なお、索引列が一意である場合、すなわち、索引が一意索引であり、検索条件が一意制約が
設定された索引列に対する等価条件である場合、
索引をスキャンするオペレーションの名称はINDEX UNIQUE SCANとなります。
このオペレーションでは、複数行の識別子(複数のROWID)が返されることはありません。
1つ(または0)のROWIDが返されます。
### TABLE ACCESS (BY INDEX ROWID)
次に TABLE ACCESS (BY INDEX ROWID)が実行されます。
| 3 | TABLE ACCESS BY INDEX ROWID| CH | 10 | 20080 | 11 (0)| 00:00:01 |
Id=4 INDEX RANGE SCAN
で得たROWID(のリスト)が示す行データを、CH表から取得します。
ROWIDは行のアドレスに相当する位置情報であるため、ROWIDがわかれば、行データが
どのブロックに格納しているかがわかります。
### NESTED LOOP
Id=1 NESTED LOOPS
の子オペレーションの実行がすべて終了したため、
次は、Id=1 0 NESTED LOOPSが実行されます。
| 1 | NESTED LOOPS | | 10 | 40120 | 20 (0)| 00:00:01 |
NESTED LOOPSオペレーションは、複数の表の行データを結合する結合オペレーションの
1つで、文字通り入れ子ループのような処理モデルに従い、結合処理を実行します。
具体的には、上側の子オペレーションで得たデータに対して、対応するデータを
下側の子オペレーションを介して取得して結合する処理となります。
上側の子オペレーションで得たデータが複数行である場合、1つの行ごとに下側の子オペレーション
の処理が実行されるため、下側の子オペレーションは複数回実行される場合があります。
このような動作は、プログラミング言語における入れ子の制御ループに似ていることから、
NESTED LOOPによばれているようです。
for (int i; i < 10; i++){ → Id=2 TABLE ACCESS FULL に対応 for (int j; j < 10; j++{ → Id=3 TABLE ACCESS BY INDEX ROWIDに対応 } } 先の例では、上側の子オペレーションであるId=2 TABLE ACCESS FULLで 得られた行数が1でしたが、仮に2だった場合は、 Id=3 TABLE ACCESS BY INDEX ROWIDは2回実行されます。 ### そのほかの結合オペレーション NESTED LOOP(ネステッドループ結合)以外の結合オペレーションとして、HASH JOIN (ハッシュ結合)、 MERGE JOIN(ソートマージ結合)の2つがありますが、これらについては追って説明します。
[…] オペレーションの一覧と最低限覚えておくべきオペレーション […]