子查询非嵌套
- 定义:where中出现in、not in、exists、not exists,CBO尝试将子查询展开消除filter的过程就是子查询非嵌套
- 为什么要消除filter?因为filter的驱动表一般是固定的,那么执行计划就是固定的,对于DBA来说,这样的sql不可控,随着时间推移系统中sql可能会越来越慢。消除filter的本意是为了使得执行计划可控,并不代表filter的性能就一定差。
- 如何产生filter?在子查询中使用hint:/*+ no_unnest / 即可,但是很少能通过 hint:/+ unnest */来消除filter,一般需要等价改写sql才能消除
- 当子查询中有固化关键字时,容易产生filter,固化关键字:union、union all、start with connect by、rownum、cube、rollup
- exists的写法中,因为子查询中有主表连接列,所以比in更容易产生filter
视图合并
- 定义:当sql语句中有内联视图(from后的子查询)或有create view创建的视图时,CBO会尝试将内联视图或视图进行等价改写,这个过程就是视图合并
- 如果发生了视图合并,执行计划中就没有view关键字,并且大概率打乱了原本的表连接顺序
- 可以使用hint:/*+ no_merge(表别名/子查询别名) / 禁止视图合并,或在子查询中直接使用hint:/+ no_merge */
- 当子查询中有固化关键字时,不会发生视图合并,固化关键字:union、union all、start with connect by、rownum、cube、rollup
谓词推入
- 当sql中包含不能合并的视图,并且视图中有谓词过滤条件(where)时,CBO会将谓词过滤条件推入视图中,这个过程叫谓词推入
- 谓词推入的目的就是尽可能早的过滤掉无用的数据,从而提升查询性能
- 视图中rownum会阻止谓词推入,在执行计划中如果view前有"*",说明有谓词没有推入视图中
- 使用hint:/*+ push_pred(视图名) */ 将连接列推入视图,执行计划中一般出现pushed predicate,并且视图一般作为嵌套循环的被驱动表
- 当sql有谓词推入并且有性能问题时,可以考虑关闭谓词推入功能(alter session set “_push_join_predicate”=false)
- 任何查询变换都不应该改变sql结果集,如果谓词推入的视图中含rownum,rownum就被改变了,相当于结果集被改变了,所以视图中rownum会阻止谓词推入