Oracle Hint no_merge(merge)、no_unnest(unnest)的作用对象陷阱
Oracle的查询转换中有两个概念,子查询展开和视图合并。
关于两者的概念这里不赘述,可以看看崔华的《基于Oracle的SQL优化》这本书。
其中,no_unnest(unnest)这个Hint显式控制是否对子查询做展开,no_unnest表示该子查询不展开,unnest表示子查询展开。
而no_merge(merge)这个Hint显式控制是否进行视图合并,no_merge表示视图不合并,merge表示试图合并。
实际上最近又遇到对子查询使用no_unnest不生效的情况了,然后使用no_merge生效的情况。
还楞了好一会才反应过来,所以决定还是记录下。
崔华的《基于Oracle的SQL优化》的P642页介绍了merge和no_merge的使用对象和作用,
MERGE是针对单个目标视图的Hint,它的含义是让优化器对目标视图执行视图合并(View Merging)。
而P643页介绍了unnest和no_unnest的使用对象和作用,
UNNEST是针对子查询的Hint,它的含义是让优化器对目标SQL中的子查询执行子查询展开(Subquery Unnesting)。
举个例子,select * from a left join (select * from m where m.id=100) b on a.id=b.id。
这条sql使用no_unnest是不生效的,比如这就是有问题的:select * from a left join (select /*+ no_unnest */ * from m where m.id=100) b on a.id=b.id。
只能使用no_merge。
搞清楚这个问题只需要理解子查询的概念:当一个查询是另一个查询的条件时,称之为子查询。
一直潜意识就认为子查询就是嵌套的另外一些SELECT查询,实际上还要满足是另外一个查询的条件。
像上边的例子,显然不满足第二点。
而且实际上可以拿一个视图名字替换掉查询块内容而不会产生语法错误,比如变成select * from a left join view_name b on a.id=b.id那么这里就起到的是”视图“的作用,因此用no_merge。
像select * from a where a.id in (select b.id from b)则无法替换掉,否则产生语法错误。比如select * from a where a.id in view_name那就有问题了。
最后引用崔华的《基于Oracle的SQL优化》P337的一段话:
Oracle数据库里子查询前的where条件如果是如下这些条件之一,那么这种类型的目标SQL在满足了一定的条件后就可以做子查询展开:
- SINGLE-ROW(即=、<、>、<=、>=和<>)
- EXISTS
- NOT EXISTS
- IN
- NOT IN
- ANY
- ALL