基于Oracle的SQL优化--学习(十四)
子查询展开
子查询展开(SubqueryUnnesting)是优化器处理带子査询的目标SQL的一种优化手段,它是指优化器不再将目标SQL中的子查询当作一个独立的处理单元来单独执行,而是将该子査询转换为它自身和外部查询之间等价的表连接。这种等价表连接转换要么是将子查询拆开(即将该子查询中的表、视图从子查询中拿出来,然后和外部査询中的表、视图做表连接),要么是不拆开但是会把该子査询转换为一个内嵌视图(InlineView),然后再和外部查询中的表、视图做表连接。
Oracle会确保子查询展开所对应的等价表连接转换的正确性,即转换后的SQL和原SQL在语义上一定是等价的。当然,不是所有的子查询都能做子查询展开,有些子查询是不能做这种等价表连接转换的,这种情况下Oracle就不会对其做子查询展开,也就是说此时Oracle还是会将该子查询当作一个独立的处理单元来单独执行。另外,在Oracle10g及其以后的版本中,对于那种不拆开子查询但是会把该子查询转换成一个内嵌视图的子查询展开(即子查询展开的第二种情形),只有当经过子查询展开后的等价改写SQL的成本值小于原SQL的成本值时,Oracle才会对原SQL执行子查询展开。
子査询展开通常都会提高原SQL的执行效率,因为如果原SQL不做子查询展开,那么通常情况下该子查询就会在其执行计划的最后一步才被执行,并且会走FILTER类型的执行计划,这也就意味着对于外部查询所在结果集中的每一条记录,该子查询都会被当作一个独立的执行单元来执行一次,外部查询所在的结果集有多少条记录,该子查询就会被执行多少次(可以近似这么理解,实际上并不完全是这样)。这种执行方式的执行效率通常情况下都不会太高,尤其是在子查询中包含两个或者两个以上表连接时,此时做子查询展开后的执行效率往往会比走FILTER类型的执行计划高很多,因为此时优化器就会有其他更多、更高效的执行路径(比如哈希连接)可以选择。
Oracle数据库里子查询前的where条件如果是如下这些条件之一,那么这种类型的目标SQL在满足了一定的条件后就可以做子查询展开:
(1)SINGLE-ROW(即=、<、>、<=、>=和?)
(2)EXISTS
(3)NOTEXISTS
(4)IN
(5)NOTIN
(6)ANY
(7)ALL
Oracle数据库里子查询前的where条件如果是如下这些条件之一,那么这种类型的目标SQL在满足了一定的条件后就可以做子查询展开:
(1)SINGLE-ROW(即=、<、>、<=、>=和?)
(2)EXISTS
(3)NOTEXISTS
(4)IN
(5)NOTIN
(6)ANY
(7)ALL
如果一个子查询前的where条件是SINGLE-ROW条件,则意味着该子查询的返回结果至多只会返回一条记录;如果该子査询前的where条件是除SINGLE-ROW条件之外的上述其他类型的条件,则该子查询的返回结果就可以包含多条记录。ANY和ALL通常和SINGLE-ROW条件联用,虽然这两个条件我们不常用,但它们的应用范围其实比EXISTS、NOTEXISTS、IN和NOTIN要广很多,比如IN实际上就相当于=ANY,NOT IN实际上就相当于<> ALL。
子査询是否能做子杳询展开取决于如下两个条件:
(1)了査询展开所对应的等价改写SQL和原SQL在语义上一定要是完全等价的。如果改写后的SQL和原SQL并不一定能保持语义上的完全等价,这种类型的子查询就不能做子查询展开
(2)对于不拆开子杳询但是会把它转换为一个内嵌视图的子査询展开,只有当经过子查询展开后的等价改写SQL的成本值小于原SQL的成本值时,Oracle才会对0标SQL执行子查询展开。
需要注意的是,对于子査询展开的第一种情形(即将子査询拆开,把该子査询中的表、视图从子査询中拿出来,然后和外部査询中的表、视图做表连接),即使是在Oracle10g及其以后的版本中,Oracle也不会考虑子査询展开的成本,即Oracle此时会认为这种情形下子査询展开的效率始终比不展开的效率要离,这也就意味着如果目标SQL满足子査询展开的第一种情形,则Oracle始终会对其做子査询展开,而不管经过子查询展开后的等价改写SQL的成本值是否小于原SQL的成本值。
(1)了査询展开所对应的等价改写SQL和原SQL在语义上一定要是完全等价的。如果改写后的SQL和原SQL并不一定能保持语义上的完全等价,这种类型的子查询就不能做子查询展开
(2)对于不拆开子杳询但是会把它转换为一个内嵌视图的子査询展开,只有当经过子查询展开后的等价改写SQL的成本值小于原SQL的成本值时,Oracle才会对0标SQL执行子查询展开。
需要注意的是,对于子査询展开的第一种情形(即将子査询拆开,把该子査询中的表、视图从子査询中拿出来,然后和外部査询中的表、视图做表连接),即使是在Oracle10g及其以后的版本中,Oracle也不会考虑子査询展开的成本,即Oracle此时会认为这种情形下子査询展开的效率始终比不展开的效率要离,这也就意味着如果目标SQL满足子査询展开的第一种情形,则Oracle始终会对其做子査询展开,而不管经过子查询展开后的等价改写SQL的成本值是否小于原SQL的成本值。