两个sql优化

手动迁移原创博客,原文发表在http://blog.itpub.net/20777547/viewspace-1144174/


1.原语句:


    SELECT *
      FROM cm_jc_f_spf_bonus_gf t
     WHERE (j_match_id, createtime) IN (
                       SELECT j_match_id, MAX (t1.createtime)
                           FROM cm_jc_f_spf_bonus_gf t1
                          WHERE lotterycode = '108'
                            AND (lotterperiod = '2014-04-17')
                       GROUP BY j_match_id, flag)


执行计划如下:

-----------------------------------------------------------------------------------------------------------
| Id  | Operation                      | Name                     | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT               |                          |     1 |    96 |  3039   (3)| 00:00:37 |
|*  1 |  HASH JOIN RIGHT SEMI          |                          |     1 |    96 |  3039   (3)| 00:00:37 |
|   2 |   VIEW                         | VW_NSO_1                 |  1631 | 29358 |    71   (2)| 00:00:01 |
|   3 |    HASH GROUP BY               |                          |  1631 | 50561 |    71   (2)| 00:00:01 |
|   4 |     TABLE ACCESS BY INDEX ROWID| CM_JC_F_SPF_BONUS_GF     |  1631 | 50561 |    70   (0)| 00:00:01 |
|*  5 |      INDEX RANGE SCAN          | IDX_CM_JC_F_SPF_BONUS_GF |  1636 |       |     9   (0)| 00:00:01 |
|   6 |   TABLE ACCESS FULL            | CM_JC_F_SPF_BONUS_GF     |  1143K|    85M|  2957   (2)| 00:00:36 |
-----------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - access("J_MATCH_ID"="$nso_col_1" AND "CREATETIME"="$nso_col_2")
   5 - access("LOTTERPERIOD"='2014-04-17' AND "LOTTERYCODE"='108')
Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
      14380  consistent gets
      12710  physical reads
          0  redo size
       3187  bytes sent via SQL*Net to client
        503  bytes received via SQL*Net from client
          3  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
         26  rows processed

因为lotterperiod和lotterycode上有个组合索引,所以子select的数据检索还是很快的,cost值只有9,主要慢在第6步的全表扫描上。本来以为在join列createtime和j_match_id上也建一个索引可以避免的,很可惜一点用也没有。后来才想到既然主表和子表都是同一个表在做join,既然子表的条件可以走索引,那么为什么不把同样的条件也加到主表上呢(询问过开发人员,这样的改动和之前的在业务需求上是等价的)。

修改后的sql:


    SELECT *
      FROM cm_jc_f_spf_bonus_gf t
     WHERE (j_match_id, createtime) IN (
                       SELECT j_match_id, MAX (t1.createtime)
                           FROM cm_jc_f_spf_bonus_gf t1
                          WHERE lotterycode = '108'
                            AND (lotterperiod = '2014-04-17')
                       GROUP BY j_match_id, flag)
       AND lotterycode = '108'
       AND (lotterperiod = '2014-04-17')


 -----------------------------------------------------------------------------------------------------------
| Id  | Operation                      | Name                     | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT               |                          |     1 |    96 |   142   (2)| 00:00:02 |
|*  1 |  HASH JOIN RIGHT SEMI          |                          |     1 |    96 |   142   (2)| 00:00:02 |
|   2 |   VIEW                         | VW_NSO_1                 |  1631 | 29358 |    71   (2)| 00:00:01 |
|   3 |    HASH GROUP BY               |                          |  1631 | 50561 |    71   (2)| 00:00:01 |
|   4 |     TABLE ACCESS BY INDEX ROWID| CM_JC_F_SPF_BONUS_GF     |  1631 | 50561 |    70   (0)| 00:00:01 |
|*  5 |      INDEX RANGE SCAN          | IDX_CM_JC_F_SPF_BONUS_GF |  1636 |       |     9   (0)| 00:00:01 |
|   6 |   TABLE ACCESS BY INDEX ROWID  | CM_JC_F_SPF_BONUS_GF     |  1631 |   124K|    70   (0)| 00:00:01 |
|*  7 |    INDEX RANGE SCAN            | IDX_CM_JC_F_SPF_BONUS_GF |  1636 |       |     9   (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - access("J_MATCH_ID"="$nso_col_1" AND "CREATETIME"="$nso_col_2")
   5 - access("LOTTERPERIOD"='2014-04-17' AND "LOTTERYCODE"='108')
   7 - access("LOTTERPERIOD"='2014-04-17' AND "LOTTERYCODE"='108')
Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
        129  consistent gets
          0  physical reads
          0  redo size
       3173  bytes sent via SQL*Net to client
        503  bytes received via SQL*Net from client
          3  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
         26  rows processed

可以看到这样修改之后cost值和逻辑读都大幅度降低了


2.另一个语句是这样的,在一张订单表上要统计各个代理商的销售额,这个本来其实很简单的,sum加group by就行了。但由于当初设计的问题,有几个代理商在系统里面有好几个不同的代理商编号,但在统计结算的时候他们都是算一家的。原先的sql是把这几个代理商单独拎出来统计然后再union all合并:


    SELECT a.cagentcode, b.cagentname, SUM (a.iamount)
        FROM cm_orderinfo_report a, cm_agent_info b
       WHERE a.cagentcode = b.cagentcode
         AND a.coperdate >= TRUNC (SYSDATE) - 1
         AND a.coperdate < TRUNC (SYSDATE)
         AND a.iscprocflag = 0
         AND a.cagentcode NOT LIKE '10771%'
         AND a.cagentcode NOT LIKE '60%'
         AND a.cagentcode != '1100'
    GROUP BY a.cagentcode, b.cagentname /*order by a.cagentcode*/
    UNION ALL
    SELECT '1100' AS cagentcode, '广西网站' AS cagentname,
             NVL (SUM (iamount), 0)
        FROM cm_orderinfo_report
       WHERE coperdate >= TRUNC (SYSDATE) - 1
         AND coperdate < TRUNC (SYSDATE)
         AND iscprocflag = 0
         AND (cagentcode = '1100' OR cagentcode LIKE '60%')
    UNION ALL
    SELECT '1077106', '广西移动', NVL (SUM (iamount), 0)
        FROM cm_orderinfo_report
       WHERE coperdate >= TRUNC (SYSDATE) - 1
         AND coperdate < TRUNC (SYSDATE)
         AND iscprocflag = 0
         AND cagentcode LIKE '10771%'
    ORDER BY cagentcode;


 -----------------------------------------------------------------------------------------------------------
| Id  | Operation                        | Name                   | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                 |                        |     4 |   122 |    30  (10)| 00:00:01 |
|   1 |  SORT ORDER BY                   |                        |     4 |   122 |    29  (63)| 00:00:01 |
|   2 |   UNION-ALL                      |                        |       |       |            |          |
|   3 |    HASH GROUP BY                 |                        |     2 |    80 |    13  (16)| 00:00:01 |
|*  4 |     FILTER                       |                        |       |       |            |          |
|*  5 |      HASH JOIN                   |                        |     2 |    80 |    12   (9)| 00:00:01 |
|*  6 |       TABLE ACCESS BY INDEX ROWID| CM_ORDERINFO_REPORT    |     4 |    84 |     8   (0)| 00:00:01 |
|*  7 |        INDEX RANGE SCAN          | INDEX_REPORT_COPERDATE |     6 |       |     3   (0)| 00:00:01 |
|*  8 |       TABLE ACCESS FULL          | CM_AGENT_INFO          |    86 |  1634 |     3   (0)| 00:00:01 |
|   9 |    SORT AGGREGATE                |                        |     1 |    21 |            |          |
|* 10 |     FILTER                       |                        |       |       |            |          |
|* 11 |      TABLE ACCESS BY INDEX ROWID | CM_ORDERINFO_REPORT    |     1 |    21 |     8   (0)| 00:00:01 |
|* 12 |       INDEX RANGE SCAN           | INDEX_REPORT_COPERDATE |     6 |       |     3   (0)| 00:00:01 |
|  13 |    SORT AGGREGATE                |                        |     1 |    21 |            |          |
|* 14 |     FILTER                       |                        |       |       |            |          |
|* 15 |      TABLE ACCESS BY INDEX ROWID | CM_ORDERINFO_REPORT    |     2 |    42 |     8   (0)| 00:00:01 |
|* 16 |       INDEX RANGE SCAN           | INDEX_REPORT_COPERDATE |     6 |       |     3   (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   4 - filter(TRUNC(    6 - filter("A"."CAGENTCODE" NOT LIKE '10771%' AND "A"."ISCPROCFLAG"=0 AND
              "A"."CAGENTCODE"<>'1100' AND "A"."CAGENTCODE" NOT LIKE '60%')
   7 - access("A"."COPERDATE">=TRUNC(SYSDATE@!)-1 AND "A"."COPERDATE"    8 - filter("B"."CAGENTCODE"<>'1100')
  10 - filter(TRUNC(   12 - access("COPERDATE">=TRUNC(SYSDATE@!)-1 AND "COPERDATE"   14 - filter(TRUNC(   16 - access("COPERDATE">=TRUNC(SYSDATE@!)-1 AND "COPERDATE" Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
      68191  consistent gets
          0  physical reads
          0  redo size
       1485  bytes sent via SQL*Net to client
        503  bytes received via SQL*Net from client
          3  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
         26  rows processed

其实如此的union案例是有一个比较经典的解决方法的,那就是用case when来代替:


    SELECT a.cagentcode, b.cagentname, a.iamount
        FROM (SELECT (CASE
                           WHEN cagentcode = '1100' OR cagentcode LIKE '60%'
                              THEN '1100'
                           WHEN cagentcode LIKE '10771%'
                              THEN '1077106'
                           ELSE cagentcode
                        END
                       ) AS cagentcode,
                       SUM (iamount) AS iamount
                  FROM cm_orderinfo_report
                 WHERE coperdate >= TRUNC (SYSDATE - 1)
                   AND coperdate < TRUNC (SYSDATE)
                   AND iscprocflag = 0
              GROUP BY (CASE
                           WHEN cagentcode = '1100' OR cagentcode LIKE '60%'
                              THEN '1100'
                           WHEN cagentcode LIKE '10771%'
                              THEN '1077106'
                           ELSE cagentcode
                        END
                       )) a,
             cm_agent_info b
       WHERE a.cagentcode = b.cagentcode
    ORDER BY a.cagentcode


 ---------------------------------------------------------------------------------------------------------
| Id  | Operation                      | Name                   | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT               |                        |     6 |   312 |    13  (16)| 00:00:01 |
|   1 |  SORT GROUP BY                 |                        |     6 |   312 |    13  (16)| 00:00:01 |
|*  2 |   FILTER                       |                        |       |       |            |          |
|*  3 |    HASH JOIN                   |                        |     6 |   312 |    12   (9)| 00:00:01 |
|*  4 |     TABLE ACCESS BY INDEX ROWID| CM_ORDERINFO_REPORT    |     6 |   126 |     8   (0)| 00:00:01 |
|*  5 |      INDEX RANGE SCAN          | INDEX_REPORT_COPERDATE |     6 |       |     3   (0)| 00:00:01 |
|   6 |     TABLE ACCESS FULL          | CM_AGENT_INFO          |    86 |  2666 |     3   (0)| 00:00:01 |
---------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - filter(TRUNC(               THEN '1100' WHEN ("CAGENTCODE" LIKE '10771%') THEN '1077106' ELSE "CAGENTCODE" END )
   4 - filter("ISCPROCFLAG"=0)
   5 - access("COPERDATE">=TRUNC(SYSDATE@!-1) AND "COPERDATE" Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
      22735  consistent gets
          0  physical reads
          0  redo size
       1482  bytes sent via SQL*Net to client
        503  bytes received via SQL*Net from client
          3  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
         26  rows processed


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值