底辺SEの備忘録

スキル無しの底辺です。参考になりません。

実行計画でNESTED LOOPSを選択した際の外部表(駆動表)の選択について

事前準備

--table作成
create table tbl1(col1 number,col2 number,col3 number);
create table tbl2(col1 number,col2 number,col3 number);

--インデックス作成
CREATE INDEX tbl1_idx ON tbl1(col1);
CREATE INDEX tbl2_idx ON tbl2(col1);

--tbl1には10件投入
begin
for i in 1..10 loop
insert into tbl1 values(i,i,i);
commit;
end loop;
end;
/

--tbl1には1000000件投入
begin
for i in 1..1000000 loop
insert into tbl2 values(i,i,i);
commit;
end loop;
end;
/

確認(読み込みブロック数と実行時間)

SQL> --tbl1(件数が少ない方を駆動表にする)
SQL> select /*+ USE_NL(tbl2) LEADING(tbl1) */count(1) from tbl1,tbl2 where tbl1.col2=tbl2.col2;

  COUNT(1)
----------
        10

経過: 00:00:00.55

実行計画
----------------------------------------------------------
Plan hash value: 1872982815

----------------------------------------------------------------------------
| Id  | Operation           | Name | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------
|   0 | SELECT STATEMENT    |      |     1 |    26 |  7508   (1)| 00:00:01 |
|   1 |  SORT AGGREGATE     |      |     1 |    26 |            |          |
|   2 |   NESTED LOOPS      |      |    10 |   260 |  7508   (1)| 00:00:01 |
|   3 |    TABLE ACCESS FULL| TBL1 |    10 |   130 |     2   (0)| 00:00:01 |
|*  4 |    TABLE ACCESS FULL| TBL2 |     1 |    13 |   751   (1)| 00:00:01 |
----------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   4 - filter("TBL1"."COL2"="TBL2"."COL2")

Note
-----
   - dynamic statistics used: dynamic sampling (level=2)


統計
----------------------------------------------------------
          9  recursive calls
          0  db block gets
      27616  consistent gets
          0  physical reads
          0  redo size
        573  bytes sent via SQL*Net to client
        451  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          2  sorts (memory)
          0  sorts (disk)
          1  rows processed

SQL> --tbl2(件数が多い方を駆動表にする)
SQL> select /*+ USE_NL(tbl1) LEADING(tbl2) */count(1) from tbl1,tbl2 where tbl1.col2=tbl2.col2;

  COUNT(1)
----------
        10

経過: 00:00:06.35

実行計画
----------------------------------------------------------
Plan hash value: 835256089

----------------------------------------------------------------------------
| Id  | Operation           | Name | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------
|   0 | SELECT STATEMENT    |      |     1 |    26 |   301K  (1)| 00:00:12 |
|   1 |  SORT AGGREGATE     |      |     1 |    26 |            |          |
|   2 |   NESTED LOOPS      |      |    10 |   260 |   301K  (1)| 00:00:12 |
|   3 |    TABLE ACCESS FULL| TBL2 |  1109K|    13M|   751   (1)| 00:00:01 |
|*  4 |    TABLE ACCESS FULL| TBL1 |     1 |    13 |     0   (0)| 00:00:01 |
----------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   4 - filter("TBL1"."COL2"="TBL2"."COL2")

Note
-----
   - dynamic statistics used: dynamic sampling (level=2)


統計
----------------------------------------------------------
          7  recursive calls
          0  db block gets
    3002836  consistent gets
          0  physical reads
          0  redo size
        573  bytes sent via SQL*Net to client
        451  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          2  sorts (memory)
          0  sorts (disk)
          1  rows processed

SQL>

まとめ

実はあまり理解できていないのですが、どちらを駆動表にしてもレコードの組み合わせの個数は同じになります。 それでもtbl1の駆動表にした方が読み込みブロック数(処理時間も)が少なくなります。

docs.oracle.com 等を確認すると駆動表をフェッチしている(最初に対象データを絞る)らしいのでその分駆動表が小さい方が速いと勝手に理解しました。