现在的位置: 首页 > 综合 > 正文

oracle嵌套循环连接外部表和内部表的识别

2012年12月31日 ⁄ 综合 ⁄ 共 8494字 ⁄ 字号 评论关闭

oracle嵌套循环连接外部表和内部表的识别

SQL> create table a1 as select * from all_objects ;
Table created
SQL> select count(*) from a1;
  COUNT(*)
----------
     49708
SQL> create table a2 as select * from a1 where rownum<=10000;
Table created
SQL> analyze table a1 computer statistics;
SQL> analyze table a2 computer statistics;

 

1 嵌套连接

select/*+use_nl(a1,a2)*/a1.object_name
from 
a1,a2
where a1.object_id=a2.object_id

下面无法看出那个是外部表和内部表,据说数量小的表或者结果集当外部表,然后读一条外部表的数据去全表扫内部表。听说倒数第二个表就是外部表。

 

已选择10000行。

已用时间:  00: 01: 09.95

执行计划

----------------------------------------------------------

Plan hash value: 2866307826
---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      | 10000 |   312K|  1338K  (2)| 04:27:46 |
|   1 |  NESTED LOOPS      |      | 10000 |   312K|  1338K  (2)| 04:27:46 |
|   2 |   TABLE ACCESS FULL| A1   | 49708 |  1359K|   140   (3)| 00:00:02 |
|*  3 |   TABLE ACCESS FULL| A2   |     1 |     4 |    27   (4)| 00:00:01 |
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   3 - filter("A1"."OBJECT_ID"="A2"."OBJECT_ID")
统计信息
----------------------------------------------------------
          1  recursive calls
          0  db block gets
    6414326  consistent gets
          0  physical reads
          0  redo size
     278675  bytes sent via SQL*Net to client
       7711  bytes received via SQL*Net from client
        668  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
      10000  rows processed

 

2 增加个索引

create index ix_a2_type on a2(object_type);
select/*+use_nl(a1,a2)*/a1.object_name 
from  a1,a2
where a1.object_id=a2.object_id
and a2.object_type='INDEX'

执行计划

----------------------------------------------------------

Plan hash value: 4121744415
-------------------------------------------------------------------------------------------
| Id  | Operation                    | Name       | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |            |   333 | 12654 | 46021   (2)| 00:09:13 |
|   1 |  NESTED LOOPS                |            |   333 | 12654 | 46021   (2)| 00:09:13 |
|   2 |   TABLE ACCESS BY INDEX ROWID| A2         |   333 |  3330 |    22   (0)| 00:00:01 |
|*  3 |    INDEX RANGE SCAN          | IX_A2_TYPE |   333 |       |     1   (0)| 00:00:01 |
|*  4 |   TABLE ACCESS FULL          | A1         |     1 |    28 |   138   (3)| 00:00:02 |
-------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   3 - access("A2"."OBJECT_TYPE"='INDEX')
   4 - filter("A1"."OBJECT_ID"="A2"."OBJECT_ID")
统计信息
----------------------------------------------------------
          1  recursive calls
          0  db block gets
     657969  consistent gets
          4  physical reads
          0  redo size
      27368  bytes sent via SQL*Net to client
       1078  bytes received via SQL*Net from client
         65  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
        956  rows processed

 

这里A1和A2调换了位置。从PLSQL DEVELOPER工具视图来看 Depth 为3的先读取A2的索引IX_A2_TYPE

select  count(a2.object_name )
from   a2
where  a2.object_type='INDEX'

为956行。那么我们可以确定这个理论 “量小的表或者结果集当外部表” 关于这条理论“倒数第二个表就是外部表”尚不清楚,

因为第一条语句的计划如果A1做外部表的话就违反了 数据量小 这条定律。

 

3增加ORDERD 提示 注意该提示是指定FROM后面的表顺序来决定连接顺序的,这次A2连接A1。

select/*+ordered use_nl(a1,a2)*/a1.object_name 
from  a2,a1
where a1.object_id=a2.object_id

 

 

已选择10000行。

已用时间:  00: 01: 13.70

执行计划

----------------------------------------------------------

Plan hash value: 3613923551
---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      | 10000 |   312K|  1381K  (2)| 04:36:16 |
|   1 |  NESTED LOOPS      |      | 10000 |   312K|  1381K  (2)| 04:36:16 |
|   2 |   TABLE ACCESS FULL| A2   | 10000 | 40000 |    29   (4)| 00:00:01 |
|*  3 |   TABLE ACCESS FULL| A1   |     1 |    28 |   138   (3)| 00:00:02 |
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   3 - filter("A1"."OBJECT_ID"="A2"."OBJECT_ID")
统计信息
----------------------------------------------------------
          1  recursive calls
          0  db block gets
    6881435  consistent gets
          0  physical reads
          0  redo size
     278675  bytes sent via SQL*Net to client
       7711  bytes received via SQL*Net from client
        668  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
      10000  rows processed

 

到这里可以确定倒数第二个就是外部表,虽然于定律发生冲突,那只是优化器得到的路径是错误,也就是执行计划是错误的,需要人工干预!

 

4 给A1表建索引

create index ix_a1_type on a1(object_type);
select/*+ use_nl(a2,a1)*/a1.object_name 
from  a2,a1
where a1.object_id=a2.object_id
and a1.object_type='SYNONYM'

该索引返回同义词行数为:20026 而下面的执行机会行数为1420行。千万别搞错了执行计划第4行的A2表Rows=1 意思是每次匹配一行数据返回。

总共要匹配1420次,当为什么是1420行呢?为什么不是20026行呢? 如果是20026 那么A2应该是10000行当外部表的资格啊

 

 

已用时间:  00: 00: 58.64

执行计划

----------------------------------------------------------

Plan hash value: 2348548291
-------------------------------------------------------------------------------------------
| Id  | Operation                    | Name       | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |            |  1420 | 56800 | 38305   (2)| 00:07:40 |
|   1 |  NESTED LOOPS                |            |  1420 | 56800 | 38305   (2)| 00:07:40 |
|   2 |   TABLE ACCESS BY INDEX ROWID| A1         |  1420 | 51120 |    63   (0)| 00:00:01 |
|*  3 |    INDEX RANGE SCAN          | IX_A1_TYPE |  1420 |       |     4   (0)| 00:00:01 |
|*  4 |   TABLE ACCESS FULL          | A2         |     1 |     4 |    27   (4)| 00:00:01 |
-------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   3 - access("A1"."OBJECT_TYPE"='SYNONYM')
   4 - filter("A1"."OBJECT_ID"="A2"."OBJECT_ID")
统计信息
----------------------------------------------------------
          1  recursive calls
          0  db block gets
    2584503  consistent gets
          0  physical reads
          0  redo size
      74804  bytes sent via SQL*Net to client
       2277  bytes received via SQL*Net from client
        174  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
       2592  rows processed

 

查看具体值

select count(a1.object_name )
from  a2,a1
where a1.object_id=a2.object_id
and a1.object_type='SYNONYM'

结果数据量是:2592

那么说执行计划这次判断正确了,索引返回来的数量少于1万行,唯独计算数量的时候偏少了。

因为它是这样算的 行数除以density 

select num_rows,distinct_keys,num_rows/distinct_keys from user_ind_statistics where index_name='IX_A1_TYPE'

NUM_ROWS

DISTINCT_KEYS

NUM_ROWS/DISTINCT_KEYS

49708

35

1420.22857142857

同样

select num_rows,distinct_keys,num_rows/distinct_keys from user_ind_statistics where index_name='IX_A2_TYPE'

NUM_ROWS

DISTINCT_KEYS

NUM_ROWS/DISTINCT_KEYS

10000

30

333.333333333333

 

结论:可以确定嵌套循环二定律是 数量少的为外部表(也就是传说中的驱动表,不专业的说法) 外部表在计划中是倒数第二个表。

 

5 再来看下

select/*+ordered use_nl(a1,a2)*/a1.object_name 
from  a2,a1
where a1.object_id=a2.object_id
and a1.object_type='SYNONYM'

 

已选择2592行。

已用时间:  00: 00: 02.93

执行计划

----------------------------------------------------------
Plan hash value: 3029564842
---------------------------------------------------------------------------------------------
| Id  | Operation                   | Name          | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |               |  1420 | 56800 | 20055   (1)| 00:04:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| A1            |     1 |    36 |     2   (0)| 00:00:01 |
|   2 |   NESTED LOOPS              |               |  1420 | 56800 | 20055   (1)| 00:04:01 |
|   3 |    TABLE ACCESS FULL        | A2            | 10000 | 40000 |    29   (4)| 00:00:01 |
|*  4 |    INDEX RANGE SCAN         | IX_A1_ID_TYPE |     1 |       |     1   (0)| 00:00:01 |
---------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   4 - access("A1"."OBJECT_ID"="A2"."OBJECT_ID" AND "A1"."OBJECT_TYPE"='SYNONYM')
统计信息
----------------------------------------------------------
          1  recursive calls
          0  db block gets
      10928  consistent gets
          0  physical reads
          0  redo size
      74804  bytes sent via SQL*Net to client
       2277  bytes received via SQL*Net from client
        174  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
       2592  rows processed

 

那么说来这个计划是错误的 索引返回1420条而A2表是1万条,强行指定连接提示也会导致错误执行计划。这里是把索引当作了内部表!扫描一万次索引。

抱歉!评论已关闭.