关于查询转换的一些简单分析(一)

在sql解析器中,在生成执行计划的时候,会在多个执行计划中选择最优的计划,在这个过程中,查询转换就是一个很重要的过程。
虽然最终的执行结果没有变化,但是从优化器的角度来看,查询转换的结果会更好。
大体来说查询转换有以下几种类型。
视图合并
子查询解嵌套
谓词推进
物化视图查询重写

比如
select *from emp where deptno in (select deptno from dept)
会在查询转换中转换为下面的样子.
select e.* from emp e,dept d where e.deptno=d.deptno;
从查询转换的角度来说,两者的查询效果是基本一致的。

--视图合并
select *
from emp e,
     (select deptno from emp) s_view
where e.deptno=s_view.deptno;

70 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 3638257876
---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |    71 |  2911 |     5  (20)| 00:00:01 |
|*  1 |  HASH JOIN         |      |    71 |  2911 |     5  (20)| 00:00:01 |
|   2 |   TABLE ACCESS FULL| EMP  |    14 |   532 |     2   (0)| 00:00:01 |
|   3 |   TABLE ACCESS FULL| EMP  |    14 |    42 |     2   (0)| 00:00:01 |
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - access("E"."DEPTNO"="DEPTNO")

来比较一下是否视图合并的结果。
select
*
from emp e,
     (select /*+no_merge*/ deptno from emp) s_view
where e.deptno=s_view.deptno

70 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 2243607326
----------------------------------------------------------------------------
| Id  | Operation           | Name | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------
|   0 | SELECT STATEMENT    |      |    71 |  3621 |     5  (20)| 00:00:01 |
|*  1 |  HASH JOIN          |      |    71 |  3621 |     5  (20)| 00:00:01 |
|   2 |   TABLE ACCESS FULL | EMP  |    14 |   532 |     2   (0)| 00:00:01 |
|   3 |   VIEW              |      |    14 |   182 |     2   (0)| 00:00:01 |
|   4 |    TABLE ACCESS FULL| EMP  |    14 |    42 |     2   (0)| 00:00:01 |
----------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - access("E"."DEPTNO"="S_VIEW"."DEPTNO")

可以比较上面两个查询的时候,会发现第二个执行计划的VIEW关键字表示,执行计划是保持原样的,会单独处理视图。
使用圆括号把子查询括起来不一定在sql执行的时候会保证子查询块会单独执行。
这个时候可以根据要求来选择是否选择视图合并。

视图合并在大体来说性能还是不错的,不过在使用order by, 聚合函数,分析函数,group等等的时候,视图合并的功能还是会受到限制或者无法启用。
而且在试图合并的功能受到限制的时候,如果硬要启用视图合并功能,可能查询的结果会有一些变化,这个时候需要你来判断是否确定要启用视图合并。
select e.ename,e.sal,v.avg_salary
from emp e,
(select deptno,avg(sal) avg_salary
from emp oe
group by deptno)v
where e.deptno=v.deptno and e.sal>1000;

12 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 269884559
-----------------------------------------------------------------------------
| Id  | Operation            | Name | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |      |    14 |   546 |     6  (34)| 00:00:01 |
|*  1 |  HASH JOIN           |      |    14 |   546 |     6  (34)| 00:00:01 |
|   2 |   VIEW               |      |     3 |    78 |     3  (34)| 00:00:01 |
|   3 |    HASH GROUP BY     |      |     3 |    21 |     3  (34)| 00:00:01 |
|   4 |     TABLE ACCESS FULL| EMP  |    14 |    98 |     2   (0)| 00:00:01 |
|*  5 |   TABLE ACCESS FULL  | EMP  |    13 |   169 |     2   (0)| 00:00:01 |
-----------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - access("E"."DEPTNO"="V"."DEPTNO")
   5 - filter("E"."SAL">1000)

 

select /*+ merge(v)*/ e.ename,e.sal,v.avg_salary
from emp e,
(select deptno,avg(sal) avg_salary
from emp oe
group by deptno)v
where e.deptno=v.deptno and e.sal>1000;

12 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 4115741206
----------------------------------------------------------------------------
| Id  | Operation           | Name | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------
|   0 | SELECT STATEMENT    |      |    68 |  2176 |     6  (34)| 00:00:01 |
|   1 |  HASH GROUP BY      |      |    68 |  2176 |     6  (34)| 00:00:01 |
|*  2 |   HASH JOIN         |      |    68 |  2176 |     5  (20)| 00:00:01 |
|*  3 |    TABLE ACCESS FULL| EMP  |    13 |   325 |     2   (0)| 00:00:01 |
|   4 |    TABLE ACCESS FULL| EMP  |    14 |    98 |     2   (0)| 00:00:01 |
----------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("E"."DEPTNO"="DEPTNO")
   3 - filter("E"."SAL">1000)

 

--子查询解嵌套
子查询嵌套和视图合并的不同之处在于视图合并是在select xxx from的部分,而子查询嵌套式在where xxxx的部分。

select *from emp where deptno in (select deptno from dept)
14 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 3956160932
--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |    14 |   532 |     2   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| EMP  |    14 |   532 |     2   (0)| 00:00:01 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter("DEPTNO" IS NOT NULL)

         
先来比较一下是否启用子查询嵌套的区别 ,如果不需要启用子查询嵌套,可以使用Hint no_unnest来引导。
select *from emp where deptno in (select /*+ no_unnest*/deptno from dept)
14 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 1783302997
------------------------------------------------------------------------------
| Id  | Operation          | Name    | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |         |     5 |   190 |     2   (0)| 00:00:01 |
|*  1 |  FILTER            |         |       |       |            |          |
|   2 |   TABLE ACCESS FULL| EMP     |    14 |   532 |     2   (0)| 00:00:01 |
|*  3 |   INDEX UNIQUE SCAN| PK_DEPT |     1 |     3 |     0   (0)| 00:00:01 |
------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter( EXISTS (SELECT /*+ NO_UNNEST */ 0 FROM "DEPT" "DEPT"
              WHERE "DEPTNO"=:B1))
   3 - access("DEPTNO"=:B1)

可以从谓语部分看出来,两个的执行计划还是有很大的不同,第二个执行计划是未启用子查询解嵌套的形式。会在子查询中走索引,然后通过exists子句来和外部查询的deptno字段连接。
而第一个执行计划直接走了一个全表扫描,对于deptno的部分直接使用条件deptno is not null来过滤了。

select *from emp outer
where outer.sal>(select avg(inner.sal) from emp inner where inner.deptno=outer.deptno)
6 rows selected.

Execution Plan
----------------------------------------------------------
Plan hash value: 1245077725
--------------------------------------------------------------------------------
| Id  | Operation            | Name    | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |         |     1 |    64 |     6  (34)| 00:00:01 |
|*  1 |  HASH JOIN           |         |     1 |    64 |     6  (34)| 00:00:01 |
|   2 |   VIEW               | VW_SQ_1 |     3 |    78 |     3  (34)| 00:00:01 |
|   3 |    HASH GROUP BY     |         |     3 |    21 |     3  (34)| 00:00:01 |
|   4 |     TABLE ACCESS FULL| EMP     |    14 |    98 |     2   (0)| 00:00:01 |
|   5 |   TABLE ACCESS FULL  | EMP     |    14 |   532 |     2   (0)| 00:00:01 |
--------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - access("ITEM_1"="OUTER"."DEPTNO")
       filter("OUTER"."SAL">"AVG(INNER.SAL)")

这个例子中子查询转换为了一个内嵌式图,然后与外部的查询合并连接,相关列成为了连接条件,子查询的剩余部分成为了内嵌视图。

sql的执行方式和下面的sql类似。
select *from emp outer,
(select inner.deptno,avg(inner.sal) avg_sal from emp inner group by inner.deptno)inner where inner.deptno=outer.deptno
and outer.sal>inner.avg_sal;

6 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 269884559
-----------------------------------------------------------------------------
| Id  | Operation            | Name | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |      |     1 |    64 |     6  (34)| 00:00:01 |
|*  1 |  HASH JOIN           |      |     1 |    64 |     6  (34)| 00:00:01 |
|   2 |   VIEW               |      |     3 |    78 |     3  (34)| 00:00:01 |
|   3 |    HASH GROUP BY     |      |     3 |    21 |     3  (34)| 00:00:01 |
|   4 |     TABLE ACCESS FULL| EMP  |    14 |    98 |     2   (0)| 00:00:01 |
|   5 |   TABLE ACCESS FULL  | EMP  |    14 |   532 |     2   (0)| 00:00:01 |
-----------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - access("INNER"."DEPTNO"="OUTER"."DEPTNO")
       filter("OUTER"."SAL">"INNER"."AVG_SAL")

关于谓词推进和物化视图的部分,稍后补充。

时间: 2024-07-30 10:52:15

关于查询转换的一些简单分析(一)的相关文章

关于查询转换的一些简单分析(二)

关于查询转换的作用,在之前的一篇博文里面也大概说了视图合并和子查询嵌套的部分.http://blog.itpub.net/23718752/viewspace-1310272/ 今天来举一个实例来分析一下. 这条sql语句是在生产环境中运行的,目前执行速度在0.1秒左右.我们来看看从查询转换的角度来看,对于同一条sql语句,反复的变化就能够折腾出不少的东西来. sql语句如下: select soc   from SERVICE_DETAILS ser  --大表,数据量过亿,做了分区 wher

关于查询转换的一些简单分析(三)

关于查询转换,已经讨论了视图合并和子查询解嵌套,还有谓词推进和物化视图查询重写也是查询转换中不可或缺的部分.-->谓词推进 这个术语听起来高大上,有点故弄玄虚的味道.其实在我们的查询中已经潜移默化的使用到了. 比如下面的这个查询.我们定义了一个子查询 v,然后在外部的过滤条件中加入了过滤条件e.deptno=10  因为e.deptno=v.deptno 所以过滤条件也可以理解为v.depetno=10 select e.ename,e.sal,v.avg_salary from emp e,

一次由查询转换引起的性能问题的分析

在优化器进行查询转换的时候,如果将内嵌视图里推入连接谓词,视图里的结果集会更小,优化器就有可能会选择Nested Loops Join 与 Index Range Scan 的方式加快数据的显示.但如果内嵌视图中存在GROUP BY,此时会发生什么情况?在10g和11g里面,Oracle的处理方式有哪些不同? 问题分析 为了解答以上问题,首先需要对SQL的基本语法与查询转换(QueryTransformation)的概念与原理有一定了解. Oracle优化器可以分为下面两种: Logical O

XML实现异构数据库间转换的实现与分析(转)

xml|数据|数据库|转换 XML实现异构数据库间转换的实现与分析 类型: XML/Biztalk 收录时间: 2002-4-8 9:20:00 XML中一种扩展的标记语言,它具有很好的扩展性标记.本文通过XML实现不同数据库的定义,实现对XML数据库的访问和异构数据库之间的互访.   关键词:XML  异构数据库  信息交换  数据库访问  1  引言   XML(Extensible  Markup  Language)它是由W3C组织于1998年2月  制定的一种通用语言规范,是SGML 

《Oracle高性能SQL引擎剖析:SQL优化与调优机制详解》一3.1 启发式查询转换

3.1 启发式查询转换 所有的启发式查询转换都是基于一套优化器内建的规则.在查询转换阶段,转换器会逐个针对这些规则对查询进行检查,确定其是否满足转换规则,一旦满足,转换器就对其进行转换. 3.1.1 简单视图合并 我们知道,视图(View)的实质就是一条查询语句.在解析阶段,语句中的每个视图都会被展开至一个查询块中.如果未做视图合并,优化器则会单独分析每个视图,并为定义视图的查询语句生成一个视图子计划.然后再分析整个查询的其他部分,并生成执行计划.在这种情况下,由于视图的执行计划和整体执行计划不

Oracle CBO几种基本的查询转换详解

Oracle CBO几种基本的查询转换详解 查询转换(Query Transformation),又称为逻辑优化(Logical Optimization),也称为软优化,即查询转换器在逻辑上对语句做一些语义等价转换,从而能使优化器生成效率更高的执行计划. 语句在被提交到Oracle后,解析器(Parser)会对SQL语句的语法.语义进行分析,并将查询中的视图展开.划分为小的查询块(Query Block).它们是嵌套或者相互关联的,而查询形式则决定了它们之间是如何关联的.这些查询块被传送给了查

Rman操作简单分析

http://www.itpub.net/245264.html Rman操作简单分析 在我的上一篇文章中为大家演示了rman 备份恢复的一个特定例子.(参考:http://www.dbanotes.net/Oracle/Rman...lfile_howto.htm)rman 对dbms_backup.restore 的一些特定调用完梢酝üebug 分析出来.通过设置debug 模式,我们可以跟踪到大量的Log,从而为分析提供一定的说明.假定我们提交如下的命令:rman target /

对SysAnti.exe病毒的简单分析

周一在2号实验楼323嵌入式实验室用U盘时发现有病毒,重启后发现刚启动时电脑就有病毒,看来还原卡没起作用.一时兴起打包了病毒文件回来研究下. 病毒文件名:SysAnti.exe 文件大小:52.5KB MD5:4B160901566108C6F89F21444CE503E7 PEID查壳信息: PEID显示是ASPack壳,这一款兼容性良好的老牌壳.不过估计用工具脱不了,OD载入时出错,看来经过特意加密防止反编译.我也懒得深入研究壳了,直接丢入虚拟机的XP,创建快照.打开Procmon和Tota

GHOST漏洞原理简单分析

继 ShellShock漏洞之后,Linux又爆出一重大漏洞.网上各种关于漏洞检查和修复的文章,但很难找到一篇讲述这个漏洞原理的.在阅读网上流传的测试代码后,写一下个人对这个漏洞的简单分析. 这应该是漏洞发布的网址,里面有最详细的解释,本人才疏学浅,只看了少部分,想深入了解的可以直接阅读,如本文有错误,请指正. http://www.openwall.com/lists/oss-security/2015/01/27/9 漏洞简介: GHOST是Linux glibc库上的一个安全漏洞,CVE编