使用with as优化SQL

开发人员丢来一个sql,说是这个SQL跑了半个多小时了,还没有出结果,让优化优化
以下SQL涉及的表已经改过名字
先来看一下这个SQL的执行计划
SQL> set lines 120 pagesize 999
SQL> explain plan for select o.id ,o.user_id ,  o.amount , o.ordertime ,o.paytime
  2    from goolen o
  3  where  o.paywayid  not in (188,182  )and  o.goolentatusid = 1
  4     and (select count(id) from goolen oo where o.user_id = oo.user_id and oo.ordertime < o.ordertime and oo.goolentatusid <>1 
  5     and oo.paywayid  in (188,182) 
  6     and  oo.ordertime > to_date('2014-11-27','yyyy-mm-dd') and oo.ordertime < to_date('2014-12-28','yyyy-mm-dd')   )>0
  7     and o.ordertime > to_date('2014-11-27','yyyy-mm-dd') and o.ordertime < to_date('2014-12-28','yyyy-mm-dd') 
  8     order by o.ordertime;

Explained.

SQL> select * from table(dbms_xplan.display);

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------
Plan hash value: 121517303

-------------------------------------------------------------------------------------------------------
| Id  | Operation                      | Name                 | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT               |                      |     1 |    40 |     8   (0)| 00:00:01 |
|*  1 |  FILTER                        |                      |       |       |            |          |
|*  2 |   TABLE ACCESS BY INDEX ROWID  | goolen               |     1 |    40 |     4   (0)| 00:00:01 |
|*  3 |    INDEX RANGE SCAN            | IND_goolen_ORDERTIME |     1 |       |     3   (0)| 00:00:01 |
|   4 |   SORT AGGREGATE               |                      |     1 |    22 |            |          |
|*  5 |    FILTER                      |                      |       |       |            |          |
|*  6 |     TABLE ACCESS BY INDEX ROWID| goolen               |     1 |    22 |     4   (0)| 00:00:01 |
|*  7 |      INDEX RANGE SCAN          | IND_goolen_ORDERTIME |     1 |       |     3   (0)| 00:00:01 |
-------------------------------------------------------------------------------------------------------

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

   1 - filter( (SELECT COUNT(*) FROM "goolen" "OO" WHERE :B1>TO_DATE(' 2014-11-27 00:00:00',
              'syyyy-mm-dd hh24:mi:ss') AND "OO"."ORDERTIME"               hh24:mi:ss') AND "OO"."ORDERTIME">TO_DATE(' 2014-11-27 00:00:00', 'syyyy-mm-dd hh24:mi:ss')
              AND "OO"."USER_ID"=:B2 AND "OO"."goolenTATUSID"<>1 AND ("OO"."PAYWAYID"=182 OR
              "OO"."PAYWAYID"=188) AND "OO"."ORDERTIME"<:b3>0)
   2 - filter("O"."goolenTATUSID"=1 AND "O"."PAYWAYID"<>182 AND "O"."PAYWAYID"<>188)
   3 - access("O"."ORDERTIME">TO_DATE(' 2014-11-27 00:00:00', 'syyyy-mm-dd hh24:mi:ss') AND
              "O"."ORDERTIME"    5 - filter(:B1>TO_DATE(' 2014-11-27 00:00:00', 'syyyy-mm-dd hh24:mi:ss'))
   6 - filter("OO"."USER_ID"=:B1 AND "OO"."goolenTATUSID"<>1 AND ("OO"."PAYWAYID"=182 OR
              "OO"."PAYWAYID"=188))
   7 - access("OO"."ORDERTIME">TO_DATE(' 2014-11-27 00:00:00', 'syyyy-mm-dd hh24:mi:ss') AND
              "OO"."ORDERTIME"        filter("OO"."ORDERTIME"<:b1>
32 rows selected.

这个表不算大,不到1000万的数据,不过由于SQL写法限制,两个结果集关联,优化器选择了FILTER操作,filter操作其实类似NL的操作,
驱动表谓词过滤后差不多也有10多万的数据,虽然被驱动表上有索引,由于会被扫描次数太多,速度肯定快不了

优化的思路有两个,可以尽可能减少子查询(被驱动表)的体积,但是最好还是改写一下sql,避免做filter操作

使用下面的写法,可以减少被驱动表的体积,但是速度上还是容忍不了,查询一次差不多还是需要40秒左右

SQL> explain plan for with t as (
  2  select o.id ,o.user_id , o.amount , o.ordertime ,o.paytime,o.goolentatusid,o.paywayid
  3  from goolen o
  4  where 
  5  o.ordertime > to_date('2014-11-27','yyyy-mm-dd') and o.ordertime < to_date('2014-12-28','yyyy-mm-dd') 
  6  ),
  7      oo as (
  8  select  /*+ materialize */ * from t where t.paywayid  in (188,182) and t.goolentatusid <>1 
  9  )
 10  select t1.id ,t1.user_id ,  t1.amount , t1.ordertime ,t1.paytime
 11    from t t1
 12  where  t1.paywayid  not in (188,182  ) 
 13     and (select count(id) from oo 
 14              where t1.user_id = oo.user_id 
 15              and oo.ordertime < t1.ordertime
 16          )>0
 17     and t1.goolentatusid = 1
 18     order by t1.ordertime;

Explained.

SQL> select * from table(dbms_xplan.display);

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------
Plan hash value: 3251867247

-------------------------------------------------------------------------------------------------------------
| Id  | Operation                     | Name                        | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |                             |     1 |    83 |    11  (10)| 00:00:01 |
|   1 |  TEMP TABLE TRANSFORMATION    |                             |       |       |            |          |
|   2 |   LOAD AS SELECT              | SYS_TEMP_0FD9D6601_ABDE85D0 |       |       |            |          |
|   3 |    TABLE ACCESS BY INDEX ROWID| goolen                      |     1 |    40 |     4   (0)| 00:00:01 |
|*  4 |     INDEX RANGE SCAN          | IND_goolen_ORDERTIME        |     1 |       |     3   (0)| 00:00:01 |
|   5 |   LOAD AS SELECT              | SYS_TEMP_0FD9D6602_ABDE85D0 |       |       |            |          |
|*  6 |    VIEW                       |                             |     1 |    40 |     2   (0)| 00:00:01 |
|   7 |     TABLE ACCESS FULL         | SYS_TEMP_0FD9D6601_ABDE85D0 |     1 |    40 |     2   (0)| 00:00:01 |
|   8 |   SORT ORDER BY               |                             |     1 |    83 |     5  (20)| 00:00:01 |
|*  9 |    FILTER                     |                             |       |       |            |          |
|* 10 |     VIEW                      |                             |     1 |    83 |     2   (0)| 00:00:01 |
|  11 |      TABLE ACCESS FULL        | SYS_TEMP_0FD9D6601_ABDE85D0 |     1 |    40 |     2   (0)| 00:00:01 |
|  12 |     SORT AGGREGATE            |                             |     1 |    35 |            |          |
|* 13 |      VIEW                     |                             |     1 |    35 |     2   (0)| 00:00:01 |
|  14 |       TABLE ACCESS FULL       | SYS_TEMP_0FD9D6602_ABDE85D0 |     1 |    40 |     2   (0)| 00:00:01 |
-------------------------------------------------------------------------------------------------------------

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

   4 - access("O"."ORDERTIME">TO_DATE(' 2014-11-27 00:00:00', 'syyyy-mm-dd hh24:mi:ss') AND
              "O"."ORDERTIME"    6 - filter(("T"."PAYWAYID"=182 OR "T"."PAYWAYID"=188) AND "T"."goolenTATUSID"<>1)
   9 - filter( (SELECT COUNT("ID") FROM  (SELECT /*+ CACHE_TEMP_TABLE ("T1") */ "C0" "ID","C1"
              "USER_ID","C2" "AMOUNT","C3" "ORDERTIME","C4" "PAYTIME","C5" "goolenTATUSID","C6" "PAYWAYID" FROM
              "SYS"."SYS_TEMP_0FD9D6602_ABDE85D0" "T1") "OO" WHERE "OO"."USER_ID"=:B1 AND "OO"."ORDERTIME"<:b2>0)
  10 - filter("T1"."PAYWAYID"<>188 AND "T1"."PAYWAYID"<>182 AND "T1"."goolenTATUSID"=1)
  13 - filter("OO"."USER_ID"=:B1 AND "OO"."ORDERTIME"<:b2>
33 rows selected.

仔细观察这个sql,其实可以把sql进行如下改写,去掉了filter操作,最终这个SQL能在2秒内出结果
SQL> explain plan for with t as (select /*+ materialize */ oo.user_id, oo.ordertime
  2            from goolen oo
  3           where  oo.goolentatusid <> 1
  4             and oo.paywayid in (188, 182)
  5             and oo.ordertime > to_date('2014-11-27', 'yyyy-mm-dd')
  6             and oo.ordertime < to_date('2014-12-28', 'yyyy-mm-dd')),
  7  t1 as (
  8  select/*+ materialize */  o.id, o.user_id, o.amount, o.ordertime, o.paytime
  9    from goolen o
 10  where o.paywayid not in (188, 182)
 11     and o.goolentatusid = 1
 12     and o.ordertime > to_date('2014-11-27', 'yyyy-mm-dd')
 13     and o.ordertime < to_date('2014-12-28', 'yyyy-mm-dd'))
 14  select distinct t1.* from t join t1 on t.user_id= t1.user_id and t.ordertime
Explained.

SQL> select * from table(dbms_xplan.display);

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------
Plan hash value: 1357376612

-------------------------------------------------------------------------------------------------------------
| Id  | Operation                     | Name                        | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |                             |     1 |    47 |    15  (20)| 00:00:01 |
|   1 |  TEMP TABLE TRANSFORMATION    |                             |       |       |            |          |
|   2 |   LOAD AS SELECT              | SYS_TEMP_0FD9D6603_ABDE85D0 |       |       |            |          |
|*  3 |    TABLE ACCESS BY INDEX ROWID| goolen                      |     1 |    22 |     4   (0)| 00:00:01 |
|*  4 |     INDEX RANGE SCAN          | IND_goolen_ORDERTIME        |     1 |       |     3   (0)| 00:00:01 |
|   5 |   LOAD AS SELECT              | SYS_TEMP_0FD9D6604_ABDE85D0 |       |       |            |          |
|*  6 |    TABLE ACCESS BY INDEX ROWID| goolen                      |     1 |    40 |     4   (0)| 00:00:01 |
|*  7 |     INDEX RANGE SCAN          | IND_goolen_ORDERTIME        |     1 |       |     3   (0)| 00:00:01 |
|   8 |   SORT UNIQUE                 |                             |     1 |    47 |     6  (34)| 00:00:01 |
|*  9 |    HASH JOIN                  |                             |     1 |    47 |     5  (20)| 00:00:01 |
|  10 |     VIEW                      |                             |     1 |    15 |     2   (0)| 00:00:01 |
|  11 |      TABLE ACCESS FULL        | SYS_TEMP_0FD9D6603_ABDE85D0 |     1 |    14 |     2   (0)| 00:00:01 |
|  12 |     VIEW                      |                             |     1 |    32 |     2   (0)| 00:00:01 |
|  13 |      TABLE ACCESS FULL        | SYS_TEMP_0FD9D6604_ABDE85D0 |     1 |    32 |     2   (0)| 00:00:01 |
-------------------------------------------------------------------------------------------------------------

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

   3 - filter("OO"."goolenTATUSID"<>1 AND ("OO"."PAYWAYID"=182 OR "OO"."PAYWAYID"=188))
   4 - access("OO"."ORDERTIME">TO_DATE(' 2014-11-27 00:00:00', 'syyyy-mm-dd hh24:mi:ss') AND
              "OO"."ORDERTIME"    6 - filter("O"."goolenTATUSID"=1 AND "O"."PAYWAYID"<>182 AND "O"."PAYWAYID"<>188)
   7 - access("O"."ORDERTIME">TO_DATE(' 2014-11-27 00:00:00', 'syyyy-mm-dd hh24:mi:ss') AND
              "O"."ORDERTIME"    9 - access("T"."USER_ID"="T1"."USER_ID")
       filter("T"."ORDERTIME"
32 rows selected.


来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/23249684/viewspace-1391084/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/23249684/viewspace-1391084/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值