转载

Oracle 如何选择update、merge



链接:http://blog.itpub.net/28602568/viewspace-1815737/

标题:Oracle 如何选择update、merge 

作者:lōττéry©版权所有[文章允许转载,但必须以链接方式注明源地址,否则追究法律责任.]


 update和merge只用于更新时,俩种方式是可互更换的,但在一些特殊情况下,俩种方式的执行效率差距很大! 
     之前Oracle SQL优化总结 有提到“update和merge的选择“,在此再实例补充下
      update适用于
      1、更改单表速度快稳定性好
      2、某字段即是过滤条件又是更新字段,且该字段有选择性很强的索引时“update A set status=1 where  id=1 and status=2 and idc in (表)
      
      merge适用于
      1、根据一张表或多表联合查询的连接条件对另外一张表进行查询,连接条件匹配上的进行UPDATE,无法匹配的执行INSERT可直接用merge实现,执行效率要高于INSERT+UPDATE;
      2、“update a set i=(select i from b where a.id=b.id) 
where not exists (select 1 from b where a.id=b.id)”not exists部分需要额外消耗,可以用merge避免;
      3、“例子1“ 情况
           merge缺点:
           不支持更改字段放入on【会报错 ORA-38104: 无法更新 ON 子句中引用的列: XX】;
           有些情况merge 过滤条件不放入on可能会有其他性能问题(例子2、3)


例子1、 

SQL:
update bill_asn t
   set t.source_no =
       ( select nvl (max(b.wmsnos), '-')
          from bill_asn a
         inner join bill_syn_out b
            on b.sys_no || b.nos = a.bill_no
           and a.bizs_type = 4
         where a.bill_no = t.bill_no);

执行计划:

Description

Object_owner

Object_name

               Cost

             Cardinality

          Bytes
UPDATE STATEMENT, GOAL = ALL_ROWS 3386798772 682546 11603282
 UPDATE USR_LMP BILL_ASN
  TABLE ACCESS FULL USR_LMP BILL_ASN 5520 682546 11603282
  SORT AGGREGATE 1 54
   NESTED LOOPS 4961 976 52704
    INDEX RANGE SCAN USR_LMP I_BILL_ASN_FPNO 3 1 16
    TABLE ACCESS FULL
USR_LMP_JK
BILL_SYN_OUT
4958
9957
378366

分析:

该sql执行时间>2小时;更新条目:
682546
执行计划 I_BILL_ASN_FPNO(索引)+BILL_SYN_OUT(表) 做 NESTED LOOPS循环每次Cardinality=1取一条数据和BILL_ASN表进行update,这样的操作执行682546次(表行数);
cost消耗:682546(表行) *4961(消耗)=3386110706  接近3386798772

优化方案:

SQL:
merge into BILL_ASN T
using (SELECT NVL( MAX(b.WMSNOS), '-' ) WMSNOS, A.BILL_NO
         FROM BILL_ASN A
        INNER JOIN BILL_SYN_OUT B
           ON B.SYS_NO || B.NOS = A.BILL_NO
          AND A.BIZS_TYPE = 4
          group by A.BILL_No) T1
on (T1.BILL_NO = T.BILL_NO )
when matched then
  update set T.SOURCE_NO = t1.WMSNOS;

执行计划:

Description

Object_owner

Object_name

Cost

Cardinality

Bytes
MERGE STATEMENT, GOAL = ALL_ROWS 20254 66902 1338040
 MERGE USR_LMP BILL_ASN
  VIEW USR_LMP
   HASH JOIN 20254 66902 19000168
    VIEW USR_LMP 7440 66902 5285258
     SORT GROUP BY 7440 66902 3612708
      HASH JOIN 6409 97597 5270238
       INDEX FAST FULL SCAN USR_LMP I_BILL_ASN_FPNO 1451 66902 1070432
       TABLE ACCESS FULL USR_LMP_JK BILL_SYN_OUT 4955 995696 37836448
    TABLE ACCESS FULL
USR_LMP
BILL_ASN
5520
682546
139921930

** merge 表之间选择的HASH JOIN,且过滤后批量更新~


例子2、 

MERGE 顺序:
多表on关联后[关联的执行计划已经选好]--> 筛选where条件...所以就算where取主键定值也不会走索引(如下)
原SQL:
MERGE INTO CS_BATCH_SEND_EVA EVA
USING (SELECT ORDER_ID, NAT_MON FROM ES_INS_REC REC) T
ON (T.ORDER_ID = EVA.ORDER_ID )
WHEN MATCHED THEN
  UPDATE
     SET EVA.IS_LEGAL      = 0 
  WHERE EVA.BATCH_SEND_EVA_ID = :B1/*主键字段*/;

执行计划:
Oracle 如何选择update、merge

优化方案:

SQL:
MERGE INTO CS_BATCH_SEND_EVA EVA
USING (SELECT ORDER_ID, NAT_MON FROM ES_INS_REC REC) T
ON (T.ORDER_ID = EVA.ORDER_ID and EVA.BATCH_SEND_EVA_ID = :B1/*主键字段*/)
WHEN MATCHED THEN
  UPDATE
     SET EVA.IS_LEGAL      = 0; 

执行计划:
Oracle 如何选择update、merge

结果:
  由原来的2表全扫,改为走pk_batch_send_eve_id 主键 先筛选后是1条记录,关联条件T.ORDER_ID = EVA.ORDER_ID使ES_INS_REC 也选择order_id索引。

 
  
例子3、 

    之前遇到过merge 的where 过滤部分写查询语句,执行计划中并没有体现子查询语句中关联的表;
      【如图】
     Oracle 如何选择update、merge

    
     若您sql及执行计划同例子3的情况,且执行很久都执行不出来,可尝试将子查询部分放入on中避免此原因引起的性能问题;
     
     【如图】

     Oracle 如何选择update、merge


  【源于本人笔记】 若有书写错误,表达错误,请指正...


此条目发表在   SQL、SQL优化篇  分类目录。将固定连接加入收藏夹。




正文到此结束
Loading...