从上述显示内容中可以看出,现在SQL的执行计划走的是对表EMP_TEMP的全表扫描,并且全表扫描进的过滤查询条件是filter(("ENAME"='CLARK' AND "JOB"='MANAGER')).显然这里Oracle做了视图合并,直接查询的视图EMP_MGR_VIEW的基表EMP_TEMP,并且把针对视图的where条件推到了视图的内部,和原先创建视图时的限制条件做了合并。
现在修改视图EMP_MGR_VIEW的定义,其创建语句中加入ROWNUM关键字,这样新创建的同名视图EMP_MGR_VIEW将不能再做视图合并:
zx@MYDB>create or replace view emp_mgr_view as select * from emp_temp where job='MANAGER' and rownum<10;
View created.
zx@MYDB>select empno,sal from emp_mgr_view where ename='CLARK';
EMPNO SAL
---------- ----------
7782 2450
从上述显示内容中可以看出,现在该SQL的执行计划中包含了关键字“VIEW”,即表明这里Oracle并没有对视图EMP_MGR_VIEW做视图合并,视图EMP_MGR_VIEW被Oracle当作一个整体来独立执行。
4. FILTER
FILTER直译过来就是过滤、筛选的意思,它是一种特殊的执行计划,所对应的执行过程就是如下三步:
得到一个驱动结果集
根据一定的过滤条件从上述驱动结果集中滤除不满足条件的记录
结果集中剩下的记录就会返回给最终用户或者继续参与一下个执行步骤。
看一个实例,还是使用上面的视图EMP_MGR_VIEW:
zx@MYDB>select empno,ename from emp where empno in (select empno from emp_mgr_view);
EMPNO ENAME
---------- ------------------------------
7566 JONES
7698 BLAKE
7782 CLARK
从上述的显示内容可以看出,现在该SQL的执行计划走的是嵌套循环连接,并没有出现我们希望的FILTER类型的执行计划。这是因为Oracle在这里做了子查询展开(Subquery Unnexting),即把子查询和它外部的SQL做了合并,转化成视图VW_NOS_1和表EMP做连接。
这里使用Hint禁掉子查询展开后重新执行上述SQL:
zx@MYDB>select empno,ename from emp where empno in (select /*+ NO_UNNEST */ empno from emp_mgr_view);
EMPNO ENAME
---------- ------------------------------
7566 JONES
7698 BLAKE
7782 CLARK
从上述显示内容中可以看出,现在该SQL走的就是我们希望的FILTER类型执行计划。
FILTER类型的执行计划实际上是种改良的嵌套循环连接,它并不像嵌套循环连接那样,驱动结果集中的有多少记录就得访问多少次被驱动表。
用一个实验验证:
zx@MYDB>select * from t1;
COL1 COL2
---------- ----
1 A
2 B
3 B
zx@MYDB>select * from t2;
COL2 COL3
---- ------
A A2
B B2
D D2
zx@MYDB>select /*+ gather_plan_statistics */ * from t1 where col2 in(select /*+ no_unnest */ col2 from t2);
COL1 COL2
---------- ----
1 A
2 B
3 B