首页 > 数据库 > Oracle > 表连接方式:嵌套循环(NESTED LOOPS)
嵌套循环的算法:驱动表返回一行数据,通过连接列传值给被驱动表,驱动表返回多少行,被驱动表就要被扫描多少次。从下图就可以得知,
nested loops有传值且必须传值的功能
(SQL语句中明明没有
DEPT表的过滤条件,但是在执行计划中却有一个等值过滤,这就是
nested loops的传值功能
)
下面根据几个实验来一起看看嵌套循环的特性与注意事项
实验一、
用scott用户下的表做测试
用了三个hint,gather_plan_statistics是为了获取A-TIME信息,use_nl是强制走NL,leading是让e强制为驱动表:
select /*+ gather_plan_statistics use_nl(e,d) leading(e)*/
e.ename, e.job, d.dname
from emp e, dept d
where e.deptno = d.deptno;
执行完sql之后,select * from table(dbms_xplan.display_cursor(null,null,'ALLSTATS LAST'));
在执行计划中,离 NESTED LOOPS 关键字最近的表就是驱动表。这里 EMP 就是驱动表, DEPT 就是被驱动表。
驱动表 EMP 扫描了一次( Id=3 , Starts=1 ),返回了 14 行数据( Id=3 , A-Row ),传值 14 次给被驱动表( Id=4 ),被驱动表扫描了 14 次( Id=4 , Id=5 , Starts=14 )。
为什么嵌套循环被驱动表的连接列要创建索引呢?因为扫描被驱动表 dept 次数为 14 次,每次需要通过 deptno 列传值,所以嵌套循环被驱动表的连接列需要创建索引。
实验二、
当两表使用外连接进行关联,如果执行计划是走嵌套循环,那么这时无法更改驱动表,驱动表会被固定为主表 ,例如下面 SQL 。
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是从表,外连接走嵌套循环的时候驱动表只能是主表。
为什么两表关联是外连接的时候,走嵌套循环无法更改驱动表呢?
因为嵌套循环需要传值,主表传值给从表之后,如果发现从表没有关联上,直接显示为 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-3054983/ ,如需转载,请注明出处,否则将追究法律责任。