连接谓词推入(Join Predicate  Pushdown)是优化器处理带视图的目标SQL的一种优化手段,它是指虽然优化器会把该SQL中视图的定义SQL语句当作一个独立单元来单独执行,但此时优化器会把原本处于该视图外部查询中和该视图之间的连接条件推入到该视图的定义SQL语句内部,这样是为了能使用上该视图内部相关基表上的索引,进而能走出基于索引的嵌套循环连接。

连接谓词推入所带来的基于索引的嵌套循环连接并不一定能走出更高效的执行计划,因为当做了连接谓词推入后,原目标SQL中的视图就和外部查询产生了关联,同时Oracle又必须将该视图的定义SQL语句当作一个独立的处理单元单独执行,这也就意味着对于外部查询所在结果集中的每一条记录,上述视图的定义SQL语句都得单独执行一次,这样一旦外部查询所在的结果集的Cardinality比较大的话,即便在执行上述视图的定义语句时能用上索引,整个SQL的执行效率也不定比不做连接谓词推入时的哈希连接或排序合并连接高。所以Oracle在做连接谓词推入时会考虑成本,只有当经过连接谓词推入后走嵌套循环连接的等价改写SQL的成本值小于原SQL的成本值时,Oracle才会对目标SQL做连接谓词推入。

Oracle是否能做连接谓词推入与目标视图的类型、该视图与外部查询之间的连接类型以及连接方法有关。到目前为止,Oracle仅仅支持对如下类型的视图做连接谓词推入。

  • 视图定义SQL语句中包含UNION ALL/UNION的视图

  • 视图定义SQL语句中包含DISTINCT的视图

  • 视图定义SQL语句中包含GROUP BY的视图

  • 和外部查询之间的连接类型是外连接的视图

  • 和外部查询之间的连接类型是反连接的视图

  • 和外部查询之间的连接类型是半连接的视图

看一个连接谓词推入的实例,创建测试表、相关索引和一个普通视图和一个带有UNION ALL的视图

scott@TEST>create table emp1 as select * from emp;Table created.scott@TEST>create table emp2 as select * from emp;Table created.scott@TEST>create index idx_emp1 on emp1(empno);Index created.scott@TEST>create index idx_emp2 on emp2(empno);Index created.scott@TEST>create or replace view emp_view as 2  select emp1.empno as empno1 from emp1;View created.scott@TEST>create or replace view emp_view_union as2  select emp1.empno as empno1 from emp13  union all4  select emp2.empno as empno1 from emp2;View created.

执行测试SQL

scott@TEST>select /*+ no_merge(emp_view) */ emp.empno2  from emp,emp_view3  where emp.empno=emp_view.empno1(+)4  and emp.ename='FORD';EMPNO
----------7902

在上面的SQL中,我们使用了no_merge hint是为了让Oracle不对视图EMP_VIEW做视图合并,这样就具备了做连接谓词推入的基本条件。这里外部查询和视图EMP_VIEW的连接条件为“emp.empno=emp_view.empno1(+)”,由于已经在视图EMP_VIEW的基表EMP1的列EMPNO上创建了索引IDX_EMP1,而且这里的连接类型又是外连接,根据前面的介绍,对于视图EMP_VIEW而言,所有能做连接谓词推入的条件都已具备,Oracle在执行上面的SQL时会考虑做连接谓词推入。如果做连接谓词推入,执行计划就会 走嵌套循环外连接并且访问视图EMP_VIEW的基表EMP1时会使用列EMPNO上的索引IDX_EMP1。

从执行计划上可以看出,Oracle在执行测试SQL时确实走的是嵌套循环外连接,并且访问视图EMP_VIEW的基表EMP1时用到了索引IDX_EMP1。而且Id=3的执行步骤上Name列的值是“EMP_VIEW”,Operation列的值是“VIEW PUSHED PREDICATE”。这说明Oracle确实没有对视图EMP_VIEW做视图合并,而是把它当作一个独立的执行单元来单独执行,并且把外部查询和视图EMP_VIEW之间的连接条件“emp.empno=emp_view.empno1(+)”推入到了视图的定义语句内部。

如果不做连接谓词推入,那Oracle在访问视图EMP_VIEW的基表EMP1时就只能做全表扫描了。在测试SQL中加入no_push_pred hint(让优化器不要对视图EMP_VIEW做连接谓词推入)再次执行

scott@TEST>select /*+ no_merge(emp_view) no_push_pred(emp_view) */ emp.empno2  from emp,emp_view3  where emp.empno=emp_view.empno1(+)4  and emp.ename='FORD';EMPNO
----------7902

执行计划已经变为了HASH JOIN OUTER,而且对EMP_VIEW的基表EMP1确实用的是全表扫描。

现在把测试SQL改一下,把EMP_VIEW用EMP_VIEW_UNION视图替换,并把连接类型改为内连接,再次执行

scott@TEST>select emp.empno2  from emp,emp_view_union3  where emp.empno=emp_view_union.empno14  and emp.ename='FORD';EMPNO
----------79027902

视图EMP_VIEW_UNION的定义SQL语句中包含UNION ALL,它本身就不能做视图合并,因而具备了做连接谓词推入的基本条件。这里外部查询和视图EMP_VIEW_UNION的连接条件为“emp.empno=emp_view_union.empno1”视图对基表上的EMPNO列都有索引,虽然这里的连接类型是内连接,但对于包含UNION ALL的视图EMP_VIEW_UNION而言,所有能作连接谓词推入的条件都已具备,意味着Oracle地执行上述SQL时做考虑做连接谓词推入。如果做连接谓词推入,那执行计划就会走嵌套循环连接,并且访问视图的基表会用上列EMPNO上的索引。

从执行计划中可以看出,Oracle走的执行计划与预想的一样。

在SQL中加入no_push_pred hint(让优化器不要对视图EMP_VIEW做连接谓词推入)再次执行

scott@TEST>select /*+ no_push_pred(emp_view_union) */emp.empno2  from emp,emp_view_union3  where emp.empno=emp_view_union.empno14  and emp.ename='FORD';EMPNO
----------79027902

从执行计划可以看出,不使用连接谓词推入,则对视图的基表做的是全表扫描。

之前提到过,Oracle在做连接谓词推入时会考虑成本,只有经过连接谓词推入后走嵌套循环连接的等价改写SQL的成本值小于原SQL的成本值时,Oracle才会对目标SQL做连接谓词推入。

现在来验证一下,在上面的SQL中加入cardinality hint,让CBO认为外围查询的结果集的Cardinality是1万,这样就会急剧增加做连接谓词推入后的嵌套循环连接的成本,如果Oracle在做连接谓词推入是确实会考虑成本,那么此时Oracle就一定不会再选择做连接谓词推入。

scott@TEST>select /*+ cardinality(emp 10000) */emp.empno2  from emp,emp_view_union3  where emp.empno=emp_view_union.empno14  and emp.ename='FORD';EMPNO
----------79027902

scott@TEST>select /*+ cardinality(emp 10000) push_pred(emp_view_union) */emp.empno2  from emp,emp_view_union3  where emp.empno=emp_view_union.empno14  and emp.ename='FORD';EMPNO
----------79027902

从上面的测试可以看出使用cardinality hint后Oracle没有选择做连接谓词推入,此时的成本为10,使用push_pred强制做连接谓词推入,看到成本为20008。这也验证了之前说的Oracle在做连接谓词推入会考虑成本。

下面再看使用了内嵌视图且连接类型为外连接的示例:

scott@TEST>select /*+ no_merge(emp_view_inline) */ emp.empno2  from emp,(select emp1.empno as empno1 from emp1) emp_view_inline3  where emp.empno=emp_view_inline.empno1(+)4  and emp.ename='FORD';EMPNO
----------7902

对于上面的SQL,所有能做连接谓词推入的条件都已具备,从执行计划中也可以看出Oracle确实也做了连接谓词推入。

再回到一开始执行的SQL,把外连接改为内连接,并在其中加入push_pred hint(让优化器对视图EMP_VIEW做连接谓词推入)和USE_NL hint

scott@TEST>select /*+ no_merge(emp_view) use_nl(emp_view) push_pred(emp_view) */ emp.empno2  from emp,emp_view3  where emp.empno=emp_view.empno14  and emp.ename='FORD';EMPNO
----------7902

从执行计划来看,Oracle没有做连接谓词推入,因为它不属于开关提到的那几种能做连接谓词推入的情形,即使使用了Hint也不行。

虽然Oracle是否能做连接谓词推入与目标视图是否能做视图合并、是否是内嵌视图没有关系,但是与目标视图的类型、与外查询之间的连接类型及连接方法是有关系的。到目前为止,Oracle里能做连接谓词推入的情形公限于开头提到的那几种类型,如果不属于这些情形,即便是看起来很简单,Oracle也不会做。

参考《基于Oracle的SQL优化》

官方文档:http://docs.oracle.com/cd/E11882_01/server.112/e41573/optimops.htm#i55050

转载于:https://blog.51cto.com/hbxztc/1905643

Oracle查询转换之连接谓词推入相关推荐

  1. Oracle NO_PUSH_PRED 不使用谓词推入

    Oracle Hint No PUSH PRED The NO_PUSH_PRED hint instructs the optimizer not to push a join predicate ...

  2. oracle 谓词是什么意思,Oracle谓词推入

    在Oracle中,谓词一般就是指where后面的那些过滤条件. 而在执行计划中,我们有时候会看到一个信息: VIEW PUSHED PREDICATE 就是传说中的谓词推入. 谓词推入是什么意思? 就 ...

  3. oracle去掉谓词推入,消除谓词推进

    今天,还是用第一篇博文的例子,看下怎么消除谓词推进.首先先上图,这是一个发生了谓词推进的例子. sql: SELECT COUNT(1) AS count FROM ( SELECT a.ykf272 ...

  4. oracle 谓词推入失效,oracle view 谓词推入

    数据量描述: select count(*) from VIW_A   --28403614   视图 select count(*) from VIW_B --3960249 视图 select c ...

  5. [20150710]11G谓词推入问题2.txt

    [20150710]11G谓词推入问题2.txt --生产系统遇到一个sql语句的问题. --生产系统的sql语句比较复杂,做一个简化的例子来说明问题.来说明自己优化遇到的困惑. --昨天看来别人的回 ...

  6. oracle中sum和count可以嵌套吗_【分享吧】Oracle查询转换

    前言 "查询转换"是Oracle解析SQL语句中重要的步骤.其原理是Oracle在解析时通过对原有SQL的等价改写,以达到较高执行效率的方式. 上图展示了SQL的执行过程,当客户提 ...

  7. VIEW PUSHED PREDICATE(谓词推入)引发的惨剧

    帮网友调SQL http://www.itpub.net/forum.php?mod=viewthread&tid=1492997&extra=pageD1%3D&page=1 ...

  8. Oracle 查询转换之子查询展开

    概念:子查询展开(Subquery Unnesting)是优化器处理带子查询的目标sql的一种优化手段,它是指优化器不再将目标sql中子查询当作一个独立的处理单元来单独执行,而是将该子查询转换为它自身 ...

  9. oracle查询中表的连接顺序 手工指定

    http://database.51cto.com/art/201010/231536.htm 对那些连接了很多表的查询,Oracle需要花费大量的时间来检测连接这些表的适当顺序. 评估表的连接顺序 ...

最新文章

  1. WebApi接口 - 响应输出xml和json
  2. Zookeeper分布式一致性原理(十一):Zookeeper在JStorm中应用
  3. Spring AbstractAutowireCapableBeanFactory
  4. html列目录带图片,根据目录下的图片的个数,往html文件填充对应数量的img标签,请问有没有实现这种需求的工具?...
  5. Qt工作笔记-使用QGraphicsItem加载图片并实现碰撞
  6. Linux I2C核心、总线与设备驱动
  7. LKT系列加密芯片如何预置openssl生成的rsa密钥完成运算(三)
  8. html5 datalist 选中option选项后的触发事件
  9. Onvif协议:IPC客户端开发之图像抓拍
  10. gradle下载与配置
  11. g2o:一种图优化的C++框架
  12. python动漫教程视频_求python的进阶教程视频_python动漫教程视频教程
  13. 朝阳医院数据处理分析实例
  14. java减号的正则_java 正则匹配 特殊字符 减号- 的情况,处理方式
  15. E45: ‘readonly‘ option is set (add ! to override)
  16. 大厂面试快问快答,10分钟搞定MySQL夺命20问,你都能接住吗?
  17. Windows中texstudio的主题代码(持续更新)
  18. Qt 之 事件总线模型
  19. 设计师必备的设计类导航网站
  20. 基于STM32F103c8t6的智能垃圾桶项目

热门文章

  1. mac上java文件如何编译_如何在Mac上用Java编译和运行程序?
  2. python数据库查询优化_Python操作数据库-查询优化
  3. 吴裕雄 python 神经网络——TensorFlow训练神经网络:不使用隐藏层
  4. JavaSE基础知识(5)—面向对象(Object类)
  5. Lighting System Design UVA 11400 (dp+思维)
  6. (转帖)开源容器集群管理系统Kubernetes架构及组件介绍
  7. 用JavaScript怎么实现页面跳转 类:具有相同特征的事物的种类。http://zhidao.baidu.com/question/133995150.html...
  8. C#中的Socket编程-TCP客户端
  9. MFC动态按钮的创建及其消息响应 和 自定义消息
  10. Android数据存储之SQLite