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。
分类:
Oracle
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?