Oracle外连接的执行计划中没有出现OUTER的例子
Oracle外连接的执行计划中没有出现OUTER的例子
前言
以左连接为例子,主要是被CBO改为为内连接了。
这里有人曾经也有这个疑问:一个奇怪的执行计划(为什么不是HASH JOIN OUTER 关联)
环境构造
create table a (id number,name varchar2(20)); create table b (id number,name varchar2(20)); insert into a values(1,'a'); insert into a values(2,'b'); insert into a values(3,null); insert into a values(4,'d'); insert into a values(5,null); insert into a values(6,'a'); insert into b values(1,'a'); insert into b values(2,'b'); insert into b values(3,'c'); insert into b values(4,null); insert into b values(5,null); insert into b values(6,'a'); commit; create index idx_a_id on a(id); create index idx_a_name on a(name); create index idx_b_id on b(id); create index idx_b_name on b(name); exec dbms_stats.gather_table_stats(ownname => 'ZKM',tabname => 'A',estimate_percent => 100,method_opt => 'FOR ALL COLUMNS SIZE REPEAT',degree => 1 ,no_invalidate => false); exec dbms_stats.gather_table_stats(ownname => 'ZKM',tabname => 'B',estimate_percent => 100,method_opt => 'FOR ALL COLUMNS SIZE REPEAT',degree => 1 ,no_invalidate => false);
现象
比如SQL语句:
select /*+ use_hash(a,b) */ a.*,b.* from a left join b on a.name=b.name where b.name='a';
21:48:37 ZKM@qadb1(733)> select /*+ use_hash(a,b) */ a.*,b.* from a left join b on a.name=b.name where b.name='a'; ID NAME ID NAME --- ---- --- ---- 6 a 1 a 1 a 1 a 6 a 6 a 1 a 6 a Elapsed: 00:00:00.02 21:48:38 ZKM@qadb1(733)> select * from table(dbms_xplan.display_cursor(null,null,'allstats last')); PLAN_TABLE_OUTPUT --------------------------------------------------------------------------------------------------------------------------------------- SQL_ID 8w2cmmcw2t2qc, child number 0 ------------------------------------- select /*+ use_hash(a,b) */ a.*,b.* from a left join b on a.name=b.name where b.name='a' Plan hash value: 1107873403 -------------------------------------------------------------------------------------------------------------------------------- | Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | OMem | 1Mem | Used-Mem | -------------------------------------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | | 4 |00:00:00.02 | 6 | | | | |* 1 | HASH JOIN | | 1 | 2 | 4 |00:00:00.02 | 6 | 2061K| 2061K| 693K (0)| | 2 | TABLE ACCESS BY INDEX ROWID| A | 1 | 1 | 2 |00:00:00.01 | 2 | | | | |* 3 | INDEX RANGE SCAN | IDX_A_NAME | 1 | 1 | 2 |00:00:00.01 | 1 | | | | | 4 | TABLE ACCESS BY INDEX ROWID| B | 1 | 1 | 2 |00:00:00.01 | 4 | | | | |* 5 | INDEX RANGE SCAN | IDX_B_NAME | 1 | 1 | 2 |00:00:00.01 | 2 | | | | -------------------------------------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - access("A"."NAME"="B"."NAME") 3 - access("A"."NAME"='a') 5 - access("B"."NAME"='a') 25 rows selected. Elapsed: 00:00:00.04
执行计划中的是HASH JOIN,而不是HASH JOIN OUTER。
就是因为实际上SQL被CBO改写成
select a.*,b.* from a , b where a.name=b.name and b.name='a';
其他的例子
select a.*,b.* from a left join b on a.name=b.name where b.name is not null; 等价于 select a.*,b.* from a,b where a.name=b.name;
select a.*,b.* from a left join b on a.name=b.name where a.name='a'; 等价于,但还是左连接 select a_new.*,b.* from (select * from a where a.name='a') a_new left join b on a_new.name=b.name;
需要注意的是:
select a.*,b.* from a left join b on a.name=b.name where b.name is null; 不等价 select a.*,b.* from a,b where a.name=b.name and b.name is null;
总结规律
从以上现象总结出左/右连接能够被CBO自动改写为内连接的条件为被驱动表(比如这里的b表)的过滤条件一定位于where后,并且条件不能为is null。