首页 > 数据库 > Oracle > 外连接嵌套循环为何无法更改驱动表
相信各位大佬在做SQL优化的时候,发现一条走外连接的SQL语句表连接方式是嵌套循环,并发现有时将从表作为驱动表执行效率可能会更高,于是兴致勃勃地使用leading(xx)的hint,发现驱动表并无变化。那么这是为什么呢,今天就展开讲讲。
要理解 外连接中嵌套循环连接无法随意更改驱动表 的问题,需要从 外连接的语义约束 和 嵌套循环的工作机制 两个核心角度分析:
外连接的语义外连接的核心是 保留某一侧表的全部行(即使另一侧无匹配):
LEFT JOIN):保留
左表的所有行,右表无匹配时用
NULL填充;RIGHT JOIN):保留
右表的所有行,左表无匹配时用
NULL填充;FULL JOIN):保留
左右表的所有行,无匹配时各自用
NULL填充(但全外连接很少用嵌套循环,这里不展开)。嵌套循环连接(NLJ)的工作机制NLJ是 外层循环驱动内层循环的连接方式:
被驱动表(内层):用驱动表的行去查询内层表(通常通过索引),返回匹配结果。 驱动表的选择直接决定了 哪些行能进入结果集(只有驱动表的行才会被外层循环处理)。
外连接的 保留逻辑 要求: 必须处理被保留表的所有行 。而嵌套循环的 驱动表 决定了 外层循环的行 ——只有用 被保留的表作为驱动表 ,才能保证其所有行都被处理,否则会 违反外连接的语义 。
下面来举个例子。
explain plan for select /*+ use_nl(d,e) leading(e) */ * from dept d left join emp e on d.deptno = e.deptno; select * from table(dbms_xplan.display);
leading(e)表示让EMP表作为驱动表。
从执行计划中我们可以看到,DEPT与EMP是采用嵌套循环进行连接的,这说明use_nl(d,e)生效了。执行计划中驱动表为DEPT,虽然设置了leading(e),但是没有生效。
为什么leading(e)没有生效呢? 因为DEPT与EMP是外连接,DEPT是主表,EMP是从表,外连接走嵌套循环的时候驱动表只能是主表。
为什么两表关联是外连接的时候,走嵌套循环无法更改驱动表呢?
DEPT作为驱动表)嵌套循环的工作流程:
DEPT表的
所有4行(左表被保留,必须全部处理);deptno,查询
emp表;NULL填充(符合左外连接语义)。order作为驱动表)如果强制用右表作为驱动表,嵌套循环的流程变为:
emp表的
4行(右表未被保留,无需全部处理);deptno,查询
DEPT表;可以简单理解为 因为嵌套循环需要传值,主表传值给从表之后,如果发现从表没有关联上,直接显示为 NULL 即可;但是如果是从表传值给主表,没关联上的数据不能传值给主表,不可能传 NULL 给主表,所以两表关联是外连接的时候,走嵌套循环驱动表只能固定为主表。
需要注意的是, 如果外连接中 从表 有过滤条件,那么此时外连接会变为内连接, 例如下面 SQL 。
select /*+ leading(e) use_nl(d,e)*/ * from dept d left join emp e on d.deptno = e.deptno where e.sal < 3000;
HINT 指定了让从表 EMP 作为嵌套循环驱动表,从执行计划中我们看到, EMP 确实是作为嵌套循环的驱动表,而且执行计划中没有 OUTER 关键字,这说明 SQL 已经变为内连接。
为什么外连接的从表有过滤条件会变成内连接呢?
因为 外连接的从表有过滤条件已经排除了从表与主表没有关联上显示为 NULL 的情况。
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/70013625/viewspace-3092681/ ,如需转载,请注明出处,否则将追究法律责任。