SQL关联方式-视图合并 (View Merging)

周末时近日有网友朋友向我提问什么是视图合并?多次看到这个名词,但是每次都是扎入到执行计划中去分析了,但是对这个概念和功能完全不清楚,干啥用的?” 对于这个概念,趁赶着这个问题,自己也好好复习一下。

一. 什么是视图合并(View Merging)?

我们通过一个SQL案例来说明,现有如下SQL:

SQL1:
select emp1.empno, emp1.ename, emp1.comm
 from emp1,
 (select emp.ename, emp.deptno
 from emp, dept
 where emp.deptno = dept.deptno) a
 where emp1.ename = a.ename

执行计划如下:

view-merge-2

我们看到,SQ中存在如下的子查询结果集,区别于标量子查询,该子查询是在from 与Where之间的,从执行计划上来看,emp表和dep表进行关联过滤限定后在再EMP1进行最后关联,也就是说如下查询结果集作为一个整体看待,这个比较符合SQL写法的认知逻辑,但是强大的Oracle CBO默认会按照如此的逻辑进行关联么?

select emp.ename, emp.deptno
 from emp, dept
 where emp.deptno = dept.deptno

可以看到,我在SQL中添加了一个 Hint:no_merge,说明此执行计划是人为控制后的产物,我们来看一下去除掉Hint,CBO默认选择什么执行计划?

view-merge-1

我们看到,CBO默认进行的Table Join方案为,EMP与EMP1进行 HASH JOIN后,最后一步进行与DEPT的JOIN工作。这便是Oracle的视图合并。

视图合并是Oracle CBO针对SQL的自动优化措施进行了查询转换,Oracle会保证转换前后的SQL语义是等价的,不再将视图内部(子查询)单独处理与执行,进行视图(子查询)进行拆分,把视图定义或子查询中的表与外部查询的表进行合并(emp与emp1),说了这么多,目的是什么,目的只有1个:让CBO有更多执行路径可以选择,更多路径成本比较,选择出一种最优的执行路径。

视图合并有诸多前提条件,但是宏观上讲,视图或子查询中不能出现UNION,MINUS等集合运算,start with connect by,rownum,cube,rollup等关键字,因为视图合并后,会影响结果集,无法确认合并后语义是否一致。

二. 视图合并的种类 (观点结论引用于著名的 《基于Oracle的SQL优化》- 崔华 著 一书):

(1)简单视图合并:

简单视图合并,是指针对那些不含外连接,以及所带视图的视图定义SQL语句中不包含distinct,group by等聚合行数的目标SQL进行视图合并;进行简单视图合并的SQL,经过CBO的等价改写,一定是小于或等于未做简单视图合并的原SQL成本值,但是如果成本值相同的情况,CBO更倾向于选择进行视图合并的执行路径。

(2)外连接视图合并:

外连接视图合并,是指针对于使用了Outer Join,并且自带视图定义的SQL语句中不含DISTINCT,GROUP BY等聚合函数的目标SQL的视图合并,当目标SQL在和外部查询的表连接时(上例的emp1.ename = a.ename)变为视图与外表之间的连接,有2个前提条件

a.视图(子查询)作为外连接的驱动表;

or

b.视图如果作为被驱动表,但是视图定义SQL语句中只包含一个表;

并且

(3)复杂视图合并:

复杂视图合并,为所带视图定义的SQL语句中含有,外连接、简单视图合并中的禁忌条件:DISTINCT与GROUP BY 的情形。对于复杂视图合并的意义在于,可以将DISTINCT与GROUP BY在视图内部(子查询)中先过滤一部分数据,试图将嫌少中间结果集。但是合并结果并不能一定带来目标SQL的性能提升,CBO选择复杂视图合并的情况:会采取合并改写后的SQL的Cost < 未合并改写后的Cost前提下。