SQL优化一例:巧用hint

Oracle的hint为DBA必须掌握的一项技能,下文中记录了用hint来解决的一个sql优化案例。

问题描述:

从10gR2迁移到11gR2后,有条SQL运行变的很慢,大概要30s左右。

select prd.delevery_order_id,
       prd.sku_desc,
       prd.item_id,
       prd.product_name,
       prd.product_num,
       prd.sal_price,
       dlo.order_status,
       to_char(dlo.order_create_time, 'yyyy-MM-dd hh24:mi:ss') as order_create_time,
       dlo.order_substatus,
       prd.storage_name,
       prd.attributes,
       dlo.trans_usr_name,
       nvl(to_char(dlo.buyer_pay_time, 'yyyy-MM-dd hh24:mi:ss'), '') as buyer_pay_time,
       nvl(to_char(dlo.seller_ship_time, 'yyyy-MM-dd hh24:mi:ss'), '') as seller_ship_time,
       nvl(to_char(dlo.complete_time, 'yyyy-MM-dd hh24:mi:ss'), '') as complete_time,
       dlo.order_type,
       co.invoice_required,
       co.invoice_content,
       co.invoice_title,
       co.invoice_address
  from checkout_order_prd prd, delevery_order dlo, ctoc_order co
 where prd.delevery_order_id = dlo.delevery_order_id
   and dlo.esc_orderid = co.esc_orderid(+)
   and (not (dlo.order_status = 9 and
        dlo.cancel_type not in (904, 906, 910, 911, 912, 913)))
   and prd.merchant_onlyid = 2
   and dlo.order_status in (4,5,6,7,8)
 order by dlo.buyer_pay_time    desc nulls last,
          prd.delevery_order_id desc,
          prd.item_id           desc;

查找原因:

查看11g中的执行计划:

执行计划
----------------------------------------------------------
Plan hash value: 903664453

------------------------------------------------------------------------------------------------------------------
| Id  | Operation                      | Name                    | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT               |                         | 41040 |  9137K|       |   158K  (3)| 00:31:39 |
|   1 |  SORT ORDER BY                 |                         | 41040 |  9137K|  9960K|   158K  (3)| 00:31:39 |
|*  2 |   HASH JOIN OUTER              |                         | 41040 |  9137K|  8584K|   156K  (3)| 00:31:15 |
|*  3 |    HASH JOIN                   |                         | 41040 |  8095K|  5416K|   100K  (2)| 00:20:03 |
|   4 |     TABLE ACCESS BY INDEX ROWID| CHECKOUT_ORDER_PRD      | 41040 |  4929K|       | 22363   (1)| 00:04:29 |
|*  5 |      INDEX RANGE SCAN          | IDX_COP_MERCHANT_ONLYID | 41040 |       |       |    97   (2)| 00:00:02 |
|*  6 |     TABLE ACCESS FULL          | DELEVERY_ORDER          |  2327K|   175M|       | 67511   (3)| 00:13:31 |
|   7 |    TABLE ACCESS FULL           | CTOC_ORDER              |  3128K|    77M|       | 49872   (4)| 00:09:59 |
------------------------------------------------------------------------------------------------------------------

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

   2 - access("DLO"."ESC_ORDERID"="CO"."ESC_ORDERID"(+))
   3 - access("PRD"."DELEVERY_ORDER_ID"="DLO"."DELEVERY_ORDER_ID")
   5 - access("PRD"."MERCHANT_ONLYID"=2)
   6 - filter(("DLO"."ORDER_STATUS"=4 OR "DLO"."ORDER_STATUS"=5 OR "DLO"."ORDER_STATUS"=6 OR
              "DLO"."ORDER_STATUS"=7 OR "DLO"."ORDER_STATUS"=8) AND ("DLO"."ORDER_STATUS"<>9 OR
              ("DLO"."CANCEL_TYPE"=904 OR "DLO"."CANCEL_TYPE"=906 OR "DLO"."CANCEL_TYPE"=910 OR "DLO"."CANCEL_TYPE"=911
              OR "DLO"."CANCEL_TYPE"=912 OR "DLO"."CANCEL_TYPE"=913)))
表的连接方式为PRD和DLO先进行hash join,然后得到的结果集再和CO进行hash join。三张表都较大,导致运行时间相当慢。


解决问题:

尝试改用NESTED LOOPS来进行连接各张表。在sql中加入hint:/*+use_nl (prd,dlo,co)*/

查看新的执行计划:

执行计划
----------------------------------------------------------
Plan hash value: 1786358075

------------------------------------------------------------------------------------------------------------------
| Id  | Operation                      | Name                    | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT               |                         | 41040 |  9137K|       |   217K  (1)| 00:43:32 |
|   1 |  SORT ORDER BY                 |                         | 41040 |  9137K|  9960K|   217K  (1)| 00:43:32 |
|   2 |   NESTED LOOPS OUTER           |                         | 41040 |  9137K|       |   215K  (1)| 00:43:08 |
|   3 |    NESTED LOOPS                |                         | 41040 |  8095K|       |   104K  (1)| 00:20:55 |
|   4 |     TABLE ACCESS BY INDEX ROWID| CHECKOUT_ORDER_PRD      | 41040 |  4929K|       | 22363   (1)| 00:04:29 |
|*  5 |      INDEX RANGE SCAN          | IDX_COP_MERCHANT_ONLYID | 41040 |       |       |    97   (2)| 00:00:02 |
|*  6 |     TABLE ACCESS BY INDEX ROWID| DELEVERY_ORDER          |     1 |    79 |       |     2   (0)| 00:00:01 |
|*  7 |      INDEX UNIQUE SCAN         | PK_DELEVERY_ORDER       |     1 |       |       |     1   (0)| 00:00:01 |
|   8 |    TABLE ACCESS BY INDEX ROWID | CTOC_ORDER              |     1 |    26 |       |     3   (0)| 00:00:01 |
|*  9 |     INDEX RANGE SCAN           | IDX_CO_ESC_ORDERID1     |     1 |       |       |     2   (0)| 00:00:01 |
------------------------------------------------------------------------------------------------------------------

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

   5 - access("PRD"."MERCHANT_ONLYID"=2)
   6 - filter(("DLO"."ORDER_STATUS"=4 OR "DLO"."ORDER_STATUS"=5 OR "DLO"."ORDER_STATUS"=6 OR
              "DLO"."ORDER_STATUS"=7 OR "DLO"."ORDER_STATUS"=8) AND ("DLO"."ORDER_STATUS"<>9 OR
              ("DLO"."CANCEL_TYPE"=904 OR "DLO"."CANCEL_TYPE"=906 OR "DLO"."CANCEL_TYPE"=910 OR "DLO"."CANCEL_TYPE"=911
              OR "DLO"."CANCEL_TYPE"=912 OR "DLO"."CANCEL_TYPE"=913)))
   7 - access("PRD"."DELEVERY_ORDER_ID"="DLO"."DELEVERY_ORDER_ID")
   9 - access("DLO"."ESC_ORDERID"="CO"."ESC_ORDERID"(+))
从执行计划上来看,该SQL的Cost和Time都要比原SQL要高,但是实际运行的效果却截然不同:

已选择628行。

已用时间:  00: 00: 01.59
实际运行时间为2s,比优化前时间缩短90%以上。


小结:

1、从10g迁移到11g,可能会造成有些SQL运行变慢的情况,怀疑是统计信息不准所致,建议重新收集统计信息。

2、执行计划并不是最准确的,可能跟实际的运行情况相差很远。

3、当CBO不能正确选择Access Paths和Join Type等时,需要用hint来进行强制指定。




你可能感兴趣的:(SQL优化)