-- 表的连接顺序
结论-
nested loops join中,驱动表被访问0或者1次,被驱动表被访问0次或者N次,N由于、驱动表返回的结果集的条数决定。
-- 构造数据
drop table t1 cascade constraints purge;
drop table t2 cascade constraints purge;
create table t1 (id number not null,n number, contents varchar2(4000));
create table t2 (id number not null,t1_id number not null,n number, contents varchar2(4000));
exec dbms_random.seed(0);
insert into t1 select rownum,rownum,dbms_random.string('a',50) from dual connect by level<=100 order by dbms_random.random;
insert into t2 select rownum,rownum,rownum,dbms_random.string('b',50) from dual connect by level<=100000 order by dbms_random.random;
commit;
select count(*) from t1;
select count(*) from t2;
set linesize 1000 --建议设置大一些,否则有些列显示不出来
alter session set statistics_level=all;
select /*+ leading(t1) use_nl(t2)*/ * from t1,t2 where t1.id=t2.t1_id;
select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));
ZBB@test>select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));
PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------------------------
SQL_ID 8ac6hucf5k4sx, child number 0
-------------------------------------
select /*+ leading(t1) use_nl(t2)*/ * from t1,t2 where t1.id=t2.t1_id
Plan hash value: 1967407726
-------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
-------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 99 |00:00:00.56 | 99516 |
| 1 | NESTED LOOPS | | 1 | 99 | 99 |00:00:00.56 | 99516 |
| 2 | TABLE ACCESS FULL| T1 | 1 | 99 | 99 |00:00:00.01 | 14 |
|* 3 | TABLE ACCESS FULL| T2 | 99 | 1 | 99 |00:00:00.56 | 99502 |
-------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - filter("T1"."ID"="T2"."T1_ID")
Note
-----
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------
- dynamic sampling used for this statement (level=2)
在上面的例子中,t1是驱动表,执行计划先访问t1,返回了99条数据,然后再访问T2,访问了99次。也即是说,驱动表访问了一次,被驱动表访问了99次
set linesize 1000
alter session set statistics_level=all;
select /*+ leading(t1) use_nl(t2)*/ * from t1,t2 where t1.id=t2.t1_id and t1.n in (17,19);
select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));
ZBB@test>select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));
PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------------------------
SQL_ID 1xfpmcbf53k4a, child number 0
-------------------------------------
select /*+ leading(t1) use_nl(t2)*/ * from t1,t2 where t1.id=t2.t1_id
and t1.n in (17,19)
Plan hash value: 1967407726
-------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
-------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 2 |00:00:00.01 | 2019 |
| 1 | NESTED LOOPS | | 1 | 2 | 2 |00:00:00.01 | 2019 |
|* 2 | TABLE ACCESS FULL| T1 | 1 | 2 | 2 |00:00:00.01 | 8 |
|* 3 | TABLE ACCESS FULL| T2 | 2 | 1 | 2 |00:00:00.01 | 2011 |
-------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter(("T1"."N"=17 OR "T1"."N"=19))
3 - filter("T1"."ID"="T2"."T1_ID")
PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------------------------
Note
-----
- dynamic sampling used for this statement (level=2)
26 rows selected.
在上面的例子中,驱动表T1访问了1次。返回2条记录,被驱动表被访问了2次。
再举个例子,有点极端
select /*+ leading(t1) use_nl(t2)*/ * from t1,t2 where t1.id=t2.t1_id and t1.n=9999999999999999;
select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));
ZBB@test>select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));
PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------------------
SQL_ID gr39nar9qdt2z, child number 0
-------------------------------------
select /*+ leading(t1) use_nl(t2)*/ * from t1,t2 where t1.id=t2.t1_id
and t1.n=9999999999999999
Plan hash value: 1967407726
-------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
-------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 0 |00:00:00.01 | 7 |
| 1 | NESTED LOOPS | | 1 | 1 | 0 |00:00:00.01 | 7 |
|* 2 | TABLE ACCESS FULL| T1 | 1 | 1 | 0 |00:00:00.01 | 7 |
|* 3 | TABLE ACCESS FULL| T2 | 0 | 1 | 0 |00:00:00.01 | 0 |
-------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("T1"."N"=9999999999999999)
3 - filter("T1"."ID"="T2"."T1_ID")
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------
Note
-----
- dynamic sampling used for this statement (level=2)
26 rows selected.
上面例子中,驱动表t1被访问了1次,被驱动表t2 被访问了0次 。
再举一个极端的例子
select /*+ leading(t1) use_nl(t2)*/ * from t1,t2 where t1.id=t2.t1_id and 1=2;
select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));
ZBB@test>select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));
PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------------
SQL_ID a2r9u1k4qkzb7, child number 0
-------------------------------------
select /*+ leading(t1) use_nl(t2)*/ * from t1,t2 where t1.id=t2.t1_id
and 1=2
Plan hash value: 521644914
----------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time |
----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 0 |00:00:00.01 |
|* 1 | FILTER | | 1 | | 0 |00:00:00.01 |
| 2 | NESTED LOOPS | | 0 | 99 | 0 |00:00:00.01 |
| 3 | TABLE ACCESS FULL| T1 | 0 | 99 | 0 |00:00:00.01 |
|* 4 | TABLE ACCESS FULL| T2 | 0 | 1 | 0 |00:00:00.01 |
----------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(NULL IS NOT NULL)
4 - filter("T1"."ID"="T2"."T1_ID")
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------
Note
-----
- dynamic sampling used for this statement (level=2)
27 rows selected.
从上面的例子,and条件1=2 根本就不会成立。所以t1根本无须访问, 直接通过访问数据字典,获取到两个表的结构就好了 。
结论-
nested loops join中,驱动表被访问0或者1次,被驱动表被访问0次或者N次,N由于、驱动表返回的结果集的条数决定。
END.