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

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

执行计划如下:
Plan hash value: 1540053094

-----------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                                | Name                   | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
-----------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                         |                        |     7 |   196 |     7  (15)| 00:00:01 |       |       |
|   1 |  NESTED LOOPS                            |                        |       |       |            |          |       |       |
|   2 |   NESTED LOOPS                           |                        |     7 |   196 |     7  (15)| 00:00:01 |       |       |
|   3 |    VIEW                                  | VW_NSO_1               |     1 |    13 |     5   (0)| 00:00:01 |       |       |
|   4 |     HASH UNIQUE                          |                        |     1 |    36 |            |          |       |       |
|   5 |      NESTED LOOPS                        |                        |       |       |            |          |       |       |
|   6 |       NESTED LOOPS                       |                        |     1 |    36 |     5   (0)| 00:00:01 |       |       |
|   7 |        PARTITION RANGE ALL               |                        |     1 |    25 |     4   (0)| 00:00:01 |     1 |    11 |
|   8 |         TABLE ACCESS BY LOCAL INDEX ROWID| AGREEMENT_RESOURCE     |     1 |    25 |     4   (0)| 00:00:01 |     1 |    11 |
|*  9 |          INDEX RANGE SCAN                | AGREEMENT_RESOURCE_1IX |     1 |       |     3   (0)| 00:00:01 |     1 |    11 |
|* 10 |        INDEX UNIQUE SCAN                 | SUBSCRIBER_PK          |     1 |       |     1   (0)| 00:00:01 |       |       |
|* 11 |       TABLE ACCESS BY INDEX ROWID        | SUBSCRIBER             |     1 |    11 |     1   (0)| 00:00:01 |       |       |
|* 12 |    INDEX RANGE SCAN                      | SERVICE_DETAILS_PK     |     9 |       |     1   (0)| 00:00:01 |       |       |
|* 13 |   TABLE ACCESS BY INDEX ROWID            | SERVICE_DETAILS        |     7 |   105 |     1   (0)| 00:00:01 |       |       |
-----------------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   9 - access("RESOURCE_VALUE"='0910600138')
  10 - access("SUBSCRIBER_NO"="AGREEMENT_NO")
  11 - filter("SUB_STATUS"='A' AND "PRIM_RESOURCE_TP"="RESOURCE_TYPE")
  12 - access("AGREEMENT_NO"="SUBSCRIBER_NO")
  13 - filter("SOC_STATUS"='A')

对应的索引信息如下。
INDEX_NAME                     TABLESPACE INDEX_TYPE UNIQUENES PAR COLUMN_LIST                    TABLE_TYPE STATUS   NUM_ROWS LAST_ANAL G
------------------------------ ---------- ---------- --------- --- ------------------------------ ---------- ------ ---------- --------- -
SERVICE_DETAILS_PK             INDXS01    NORMAL     UNIQUE    NO  AGREEMENT_NO,SOC,SOC_SEQ_NO    TABLE      VALID   117595228 23-OCT-14 N

INDEX_NAME                     TABLESPACE INDEX_TYPE UNIQUENES PAR COLUMN_LIST                    TABLE_TYPE STATUS   NUM_ROWS LAST_ANAL G
------------------------------ ---------- ---------- --------- --- ------------------------------ ---------- ------ ---------- --------- -
SUBSCRIBER_1IX                 INDXM01    NORMAL     NONUNIQUE NO  CH_NODE_ID                     TABLE      VALID     6994331 23-OCT-14 N
SUBSCRIBER_2IX                 INDXM01    NORMAL     NONUNIQUE NO  EXTERNAL_ID                    TABLE      VALID     6994329 23-OCT-14 N
SUBSCRIBER_3IX                 INDXM01    NORMAL     NONUNIQUE NO  CUSTOMER_ID,SUB_STATUS         TABLE      VALID     6994331 23-OCT-14 N
SUBSCRIBER_PK                  INDXM01    NORMAL     UNIQUE    NO  SUBSCRIBER_NO                  TABLE      VALID     6994331 23-OCT-14 N

INDEX_NAME                     TABLESPACE INDEX_TYPE UNIQUENES PAR COLUMN_LIST                    TABLE_TYPE STATUS   NUM_ROWS LAST_ANAL G
------------------------------ ---------- ---------- --------- --- ------------------------------ ---------- ------ ---------- --------- -
AGREEMENT_RESOURCE_1IX                    NORMAL     NONUNIQUE YES RESOURCE_VALUE,RESOURCE_TYPE   TABLE      N/A      25762156 22-OCT-14 N
AGREEMENT_RESOURCE_2IX                    NORMAL     NONUNIQUE YES FROM_RESOURCE_VAL,RESOURCE_TYPE TABLE      N/A      25762156 22-OCT-14 N                                                                   
AGREEMENT_RESOURCE_3IX                    NORMAL     NONUNIQUE YES RESOURCE_SCOPE_ID              TABLE      N/A             0 22-OCT-14 N
AGREEMENT_RESOURCE_PK                     NORMAL     UNIQUE    YES AGREEMENT_NO,RESRC_SEQ_NO,AGREEMENT_KEY  TABLE      N/A      25762156 22-OCT-14 N

                                                                   

这个查询能够从千万级的记录中耗时0.1秒就查出数据,确实是比较理想的。我们来试着折腾一下这条sql语句。
-->改动第一步,从in 切换为exists
我们先从语句的等价性来看,in切换为exists,标黄部分为变化的部分,语句的效果是等价的。
select soc
  from SERVICE_DETAILS ser
where exists  (select subscriber_no
                          from subscriber
                         where ser.agreement_no=subscriber.subscriber_no
          and sub_status = 'A'
                           and (subscriber_no, prim_resource_tp) in (select agreement_no, resource_type from agreement_resource where resource_value='0910600138')
                           )
   and soc_status = 'A'
但是执行计划就让人大失所望。
Plan hash value: 3038362059
-----------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                            | Name                  | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                     |                       |     7 |   105 |   300M  (1)|999:59:59 |       |       |
|*  1 |  FILTER                              |                       |       |       |            |          |       |       |
|*  2 |   TABLE ACCESS FULL                  | SERVICE_DETAILS       |    96M|  1384M|   530K  (1)| 01:46:06 |       |       |
|   3 |   NESTED LOOPS                       |                       |     1 |    36 |     3   (0)| 00:00:01 |       |       |
|*  4 |    TABLE ACCESS BY INDEX ROWID       | SUBSCRIBER            |     1 |    11 |     1   (0)| 00:00:01 |       |       |
|*  5 |     INDEX UNIQUE SCAN                | SUBSCRIBER_PK         |     1 |       |     1   (0)| 00:00:01 |       |       |
|   6 |    PARTITION RANGE ALL               |                       |     1 |    25 |     2   (0)| 00:00:01 |     1 |    11 |
|*  7 |     TABLE ACCESS BY LOCAL INDEX ROWID| AGREEMENT_RESOURCE    |     1 |    25 |     2   (0)| 00:00:01 |     1 |    11 |
|*  8 |      INDEX RANGE SCAN                | AGREEMENT_RESOURCE_PK |     4 |       |     2   (0)| 00:00:01 |     1 |    11 |
------------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter( EXISTS (SELECT 0 FROM "PRDAPPO"."SUBSCRIBER" "SUBSCRIBER","PRDAPPO"."AGREEMENT_RESOURCE"
              "AGREEMENT_RESOURCE" WHERE "AGREEMENT_NO"=:B1 AND "RESOURCE_VALUE"='0910600138' AND
              "PRIM_RESOURCE_TP"="RESOURCE_TYPE" AND "SUBSCRIBER"."SUBSCRIBER_NO"=:B2 AND "SUB_STATUS"='A'))
   2 - filter("SOC_STATUS"='A')
   4 - filter("SUB_STATUS"='A')
   5 - access("SUBSCRIBER"."SUBSCRIBER_NO"=:B1)
   7 - filter("RESOURCE_VALUE"='0910600138' AND "PRIM_RESOURCE_TP"="RESOURCE_TYPE")
   8 - access("AGREEMENT_NO"=:B1)
发现了全表扫描,而且资源消耗很高。时间消耗已经得到了理论的最高值。
可见,在某些场景中,exists和in还是有所不同,在优化器分析的过程中还是对一些细节做了区别处理。稍后来解释。

-->改动第二步,去除in,exists
我们尝试去除语句中的in部分。直接把相关的表放在from之后。查询条件也等价保持不变。这种方法看似没有经过任何的调优,但是从查询转换的角度来说,可能oracle更能够合理的做出分析。

select  ser.soc
  from subscriber sub,agreement_resource agr,SERVICE_DETAILS ser
where  sub.sub_status = 'A' and sub.subscriber_no=agr.agreement_no and sub.prim_resource_tp= agr.resource_type  and resource_value='0910600138'
and  ser.agreement_no =sub.subscriber_no
   and ser.soc_status = 'A'

执行计划如下。
Plan hash value: 3783316108
--------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                             | Name                   | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
--------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                      |                        |     8 |   408 |     6   (0)| 00:00:01 |       |       |
|   1 |  NESTED LOOPS                         |                        |       |       |            |          |       |       |
|   2 |   NESTED LOOPS                        |                        |     8 |   408 |     6   (0)| 00:00:01 |       |       |
|   3 |    NESTED LOOPS                       |                        |     1 |    36 |     5   (0)| 00:00:01 |       |       |
|   4 |     PARTITION RANGE ALL               |                        |     1 |    25 |     4   (0)| 00:00:01 |     1 |    11 |
|   5 |      TABLE ACCESS BY LOCAL INDEX ROWID| AGREEMENT_RESOURCE     |     1 |    25 |     4   (0)| 00:00:01 |     1 |    11 |
|*  6 |       INDEX RANGE SCAN                | AGREEMENT_RESOURCE_1IX |     1 |       |     3   (0)| 00:00:01 |     1 |    11 |
|*  7 |     TABLE ACCESS BY INDEX ROWID       | SUBSCRIBER             |     1 |    11 |     1   (0)| 00:00:01 |       |       |
|*  8 |      INDEX UNIQUE SCAN                | SUBSCRIBER_PK          |     1 |       |     1   (0)| 00:00:01 |       |       |
|*  9 |    INDEX RANGE SCAN                   | SERVICE_DETAILS_PK     |     9 |       |     1   (0)| 00:00:01 |       |       |
|* 10 |   TABLE ACCESS BY INDEX ROWID         | SERVICE_DETAILS        |     7 |   105 |     1   (0)| 00:00:01 |       |       |
--------------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   6 - access("RESOURCE_VALUE"='0910600138')
   7 - filter("SUB"."SUB_STATUS"='A' AND "SUB"."PRIM_RESOURCE_TP"="AGR"."RESOURCE_TYPE")
   8 - access("SUB"."SUBSCRIBER_NO"="AGR"."AGREEMENT_NO")
   9 - access("SER"."AGREEMENT_NO"="SUB"."SUBSCRIBER_NO")
  10 - filter("SER"."SOC_STATUS"='A')

-->改动第三步,从视图合并的角度调整
从视图合并的角度来看,可以使用下面的方式进行调整,定义一个子查询,然后再和service_details关联。
select  ser.soc
  from 
(select subscriber_no
                          from subscriber
                         where sub_status = 'A'
                           and (subscriber_no, prim_resource_tp) in (select agreement_no, resource_type from agreement_resource where resource_value='0910600138')) temp,
SERVICE_DETAILS ser
where agreement_no =temp.subscriber_no
   and soc_status = 'A'

执行计划如下:
Plan hash value: 3927794511
---------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                              | Name                   | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
---------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                       |                        |     8 |   408 |     7  (15)| 00:00:01 |       |       |
|   1 |  NESTED LOOPS                          |                        |       |       |            |          |       |       |
|   2 |   NESTED LOOPS                         |                        |     8 |   408 |     7  (15)| 00:00:01 |       |       |
|   3 |    NESTED LOOPS                        |                        |     1 |    36 |     6  (17)| 00:00:01 |       |       |
|   4 |     SORT UNIQUE                        |                        |     1 |    25 |     4   (0)| 00:00:01 |       |       |
|   5 |      PARTITION RANGE ALL               |                        |     1 |    25 |     4   (0)| 00:00:01 |     1 |    11 |
|   6 |       TABLE ACCESS BY LOCAL INDEX ROWID| AGREEMENT_RESOURCE     |     1 |    25 |     4   (0)| 00:00:01 |     1 |    11 |
|*  7 |        INDEX RANGE SCAN                | AGREEMENT_RESOURCE_1IX |     1 |       |     3   (0)| 00:00:01 |     1 |    11 |
|*  8 |     TABLE ACCESS BY INDEX ROWID        | SUBSCRIBER             |     1 |    11 |     1   (0)| 00:00:01 |       |       |
|*  9 |      INDEX UNIQUE SCAN                 | SUBSCRIBER_PK          |     1 |       |     1   (0)| 00:00:01 |       |       |
|* 10 |    INDEX RANGE SCAN                    | SERVICE_DETAILS_PK     |     9 |       |     1   (0)| 00:00:01 |       |       |
|* 11 |   TABLE ACCESS BY INDEX ROWID          | SERVICE_DETAILS        |     7 |   105 |     1   (0)| 00:00:01 |       |       |
---------------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   7 - access("RESOURCE_VALUE"='0910600138')
   8 - filter("SUB_STATUS"='A' AND "PRIM_RESOURCE_TP"="RESOURCE_TYPE")
   9 - access("SUBSCRIBER_NO"="AGREEMENT_NO")
  10 - access("AGREEMENT_NO"="SUBSCRIBER_NO")
  11 - filter("SOC_STATUS"='A')
-->改动第4步,从子查询解嵌套的角度调整
回到exists的那个问题,执行的时候,exists的性能要比in差很远(在这个例子中,需要视业务场景而定)
我们看看怎么调整让它也快起来。我们加入hint,使得子查询解嵌套被禁用。优先从子查询中先输出数据。
select soc
  from SERVICE_DETAILS ser
where exists  (select /*+ UNNEST */ subscriber_no
                          from subscriber
                         where ser.agreement_no=subscriber.subscriber_no
          and sub_status = 'A'
                           and (subscriber_no, prim_resource_tp) in (select agreement_no, resource_type from agreement_resource where resource_value='0910600138')
                           )
   and soc_status = 'A'

执行计划如下。可以看到又得到了我们预期的效果。
Plan hash value: 2142047497
-----------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                                | Name                   | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
-----------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                         |                        |     7 |   196 |     7  (15)| 00:00:01 |       |       |
|   1 |  NESTED LOOPS                            |                        |       |       |            |          |       |       |
|   2 |   NESTED LOOPS                           |                        |     7 |   196 |     7  (15)| 00:00:01 |       |       |
|   3 |    VIEW                                  | VW_SQ_1                |     1 |    13 |     5   (0)| 00:00:01 |       |       |
|   4 |     HASH UNIQUE                          |                        |     1 |    36 |            |          |       |       |
|   5 |      NESTED LOOPS                        |                        |       |       |            |          |       |       |
|   6 |       NESTED LOOPS                       |                        |     1 |    36 |     5   (0)| 00:00:01 |       |       |
|   7 |        PARTITION RANGE ALL               |                        |     1 |    25 |     4   (0)| 00:00:01 |     1 |    11 |
|   8 |         TABLE ACCESS BY LOCAL INDEX ROWID| AGREEMENT_RESOURCE     |     1 |    25 |     4   (0)| 00:00:01 |     1 |    11 |
|*  9 |          INDEX RANGE SCAN                | AGREEMENT_RESOURCE_1IX |     1 |       |     3   (0)| 00:00:01 |     1 |    11 |
|* 10 |        INDEX UNIQUE SCAN                 | SUBSCRIBER_PK          |     1 |       |     1   (0)| 00:00:01 |       |       |
|* 11 |       TABLE ACCESS BY INDEX ROWID        | SUBSCRIBER             |     1 |    11 |     1   (0)| 00:00:01 |       |       |
|* 12 |    INDEX RANGE SCAN                      | SERVICE_DETAILS_PK     |     9 |       |     1   (0)| 00:00:01 |       |       |
|* 13 |   TABLE ACCESS BY INDEX ROWID            | SERVICE_DETAILS        |     7 |   105 |     1   (0)| 00:00:01 |       |       |
-----------------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   9 - access("RESOURCE_VALUE"='0910600138')
  10 - access("SUBSCRIBER_NO"="AGREEMENT_NO")
  11 - filter("SUB_STATUS"='A' AND "PRIM_RESOURCE_TP"="RESOURCE_TYPE")
  12 - access("SER"."AGREEMENT_NO"="ITEM_0")
  13 - filter("SOC_STATUS"='A')

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

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

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

在sql解析器中,在生成执行计划的时候,会在多个执行计划中选择最优的计划,在这个过程中,查询转换就是一个很重要的过程. 虽然最终的执行结果没有变化,但是从优化器的角度来看,查询转换的结果会更好. 大体来说查询转换有以下几种类型.视图合并子查询解嵌套谓词推进物化视图查询重写 比如 select *from emp where deptno in (select deptno from dept) 会在查询转换中转换为下面的样子. select e.* from emp e,dept d where

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

关于查询转换,已经讨论了视图合并和子查询解嵌套,还有谓词推进和物化视图查询重写也是查询转换中不可或缺的部分.-->谓词推进 这个术语听起来高大上,有点故弄玄虚的味道.其实在我们的查询中已经潜移默化的使用到了. 比如下面的这个查询.我们定义了一个子查询 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

网口扫盲二:Mac与Phy组成原理的简单分析

网口扫盲二:Mac与Phy组成原理的简单分析 1. general 下图是网口结构简图.网口由CPU.MAC和PHY三部分组成.DMA控制器通常属于CPU的一部分,用虚线放在这里是为了表示DMA控制器可能会参与到网口数据传输中. 对于上述的三部分,并不一定都是独立的芯片,根据组合形式,可分为下列几种类型: CPU集成MAC与PHY; CPU集成MAC,PHY采用独立芯片; CPU不集成MAC与PHY,MAC与PHY采用集成芯片; 本例中选用方案二做进一步说明,因为CPU总线接口很常见,通常都会做

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

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

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)的实质就是一条查询语句.在解析阶段,语句中的每个视图都会被展开至一个查询块中.如果未做视图合并,优化器则会单独分析每个视图,并为定义视图的查询语句生成一个视图子计划.然后再分析整个查询的其他部分,并生成执行计划.在这种情况下,由于视图的执行计划和整体执行计划不

性能优化之查询转换 - 子查询类

子查询,是SQL中常见的一种写法.对于优化器来说,子查询是较难优化的部分.Oracle提供了多种方式,对子查询进行查询转换. 一.子查询推进 子查询推进(又称子查询推入)是指优化器将子查询提前进行评估,使得优化器可以更早地介入优化以获得更优质的执行计划.这个技术可以通过提示PUSH_SUBQ/NO_PUSH_SUBQ控制.下面通过一个示例看看结果. SQL> create table t_users as select * from dba_users; //表已创建 SQL> create

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 /