sql 关联使用id还是code_SQL之美第五篇:merge into优化

这是一次快速优化的小case,欢迎喜欢SQL的各位朋友看看放松下。

本周给事业部出报表时,发现了业务问题数据,经过与负责的研发同学沟通后,获得了研发同学提供的问题数据修复SQL(只提取了有问题的部分):

update arc_mgr_detail t set t.dossier_count_t=(select max(d.check_count) from arc_apply_detail d

where t.is_del=0 and d.is_del=0 and t.contract_no=d.contract_no and t.paylist_code=d.paylist_code

and t.arc_receipt_detail_id=d.arc_receipt_detail_id 

and t.dossier_count_t=0 and d.check_count>0)

where t.id in (select t.id from arc_mgr_detail t ,arc_apply_detail d

where t.is_del=0 and d.is_del=0 and t.contract_no=d.contract_no and t.paylist_code=d.paylist_code

and t.arc_receipt_detail_id=d.arc_receipt_detail_id 

and t.dossier_count_t=0 and d.check_count>0);

执行计划如下:

19f57ecea733a18e91ffec3c8f5bd5ba.png

b8801e81525e8b97e115942f2acae060.png

执行了10分钟没有跑出来,眉头一皱,好玩了, 又到了优化时刻!

之前记得在公司SQL培训中多次说过,关联更新百万级以下数据,update直接改成merge into写法就好了,说干就干

(记住MERGE INTO 基础语法,不用看SQL业务逻辑 直接套)

merge into (select arc_receipt_detail_id,contract_no,dossier_count_t,t.paylist_code 

 from  arc_mgr_detail t where  t.id (select  t1.id

                  from arc_mgr_detail t1, arc_apply_detail d1

                 where t1.is_del = 0

                   and d1.is_del = 0

                   and t1.contract_no = d1.contract_no

                   and t1.paylist_code = d1.paylist_code

                   and t1.arc_receipt_detail_id = d1.arc_receipt_detail_id

                   and t1.dossier_count_t = 0

                   and d1.check_count > 0)   

                  ) t3

using (select     max(d2.check_count) MCOUNT ,D2.contract_no,D2.arc_receipt_detail_id,D2.paylist_code

          from arc_apply_detail d2, arc_mgr_detail t2

                 where t2.is_del = 0

                   and d2.is_del = 0

                   and t2.contract_no = d2.contract_no

                   and t2.paylist_code = d2.paylist_code

                   and t2.arc_receipt_detail_id = d2.arc_receipt_detail_id

                   and t2.dossier_count_t = 0

                   and d2.check_count > 0   

           group by D2.contract_no,D2.arc_receipt_detail_id,D2.paylist_code)  d3

on (t3.contract_no = d3.contract_no and  t3.arc_receipt_detail_id = d3.arc_receipt_detail_id

and t3.paylist_code = d3.paylist_code)

   when matched then 

   update set t3.dossier_count_t =d3.MCOUNT;

咔咔咔 几分钟改完,一副好像没问题的样子,让我们先看一眼改完后的执行计划:

95cfdb9cea5a2622910ff14e730624e3.png

d2db31c60bf14f83314defff1684bc44.png

什么,TIME居然999?  虽然是假的,大部分情况下还是能说明这个SQL的性能糟糕程度的。

抱着万一的希望,执行了一波。

e4ec2524ad5be6e8a271c442b9a4a7aa.png

果然,7分钟还没跑完,奇迹木有发生。是时候认真看下了:

首先,让我们拆一波 看看数据量:

cf9b31dc23f4c8d4dd2f8297ccfa29b6.png

要修改的数据只有568条,查询速度还凑合(只针对本次一次性更新操作而言),去掉统计后的执行计划如下:

6fcfd13d24dd02d5057944ab9c4c07f9.png

可以看出都是全表扫描速度依然很快。

12eec91caade8ab5abf6791754db0d32.png下面对应的条件也只有564条,拆开来每个部分速度都很快。

去掉统计后的执行计划如下:

9281176fb82c2a794554196443fadc48.png

这时候,对比前面time 999的执行计划,老司机们基本都看出问题所在了:

3648bb8b9effb56bb923a3dc2cc18a90.png

这一步计算出现的性能问题,可以看到他没有谓词信息,那么要看它到底对应SQL哪一步,我们就需要明白整体SQL的表检索链接顺序:

首先是d3里面的表先行计算完毕,8976105 形成一个视图

然后是t表传值到t1表(这就是隐含谓词推入),然后t1表跟d1表进行hash关联 14 13 15 12

接下来是t1与d1的关联结果集作为谓词推入前面的视图中先行计算11

最后就是计算完成的结果跟t表进行NL半连接更新啦

说到这里,老司机们基本很明确了。是的 就是传值搞的事儿,t表的值在执行计划里面居然被估算成1了!(当然。只是估测,这一波如果不准,就需要AE-ROWS执行计划干了,还好。这里很顺利)

那么如何阻止他们之间的传值关系呢?

一般的人直接就 NO_MERGE/NO_UNNEST干一下,想着 让T1跟D1自己先关联 别直接找T关联了,思路是好的。

然而, ORACLE的CBO这时候很[机智],你rows预估=1 我一定要主键传值进来。

为了阻止它,我们可以使用hint  full,让t1不走索引了,这样就无法传值进来了。

merge into (select arc_receipt_detail_id,contract_no,dossier_count_t,t.paylist_code 

 from  arc_mgr_detail t where  t.id in (select /*+ FULL(t1)  */t1.id

                  from arc_mgr_detail t1, arc_apply_detail d1

                 where t1.is_del = 0

                   and d1.is_del = 0

                   and t1.contract_no = d1.contract_no

                   and t1.paylist_code = d1.paylist_code

                   and t1.arc_receipt_detail_id = d1.arc_receipt_detail_id

                   and t1.dossier_count_t = 0

                   and d1.check_count > 0)   

                  ) t3

using (select     max(d2.check_count) MCOUNT ,D2.contract_no,D2.arc_receipt_detail_id,D2.paylist_code

          from arc_apply_detail d2, arc_mgr_detail t2

                 where t2.is_del = 0

                   and d2.is_del = 0

                   and t2.contract_no = d2.contract_no

                   and t2.paylist_code = d2.paylist_code

                   and t2.arc_receipt_detail_id = d2.arc_receipt_detail_id

                   and t2.dossier_count_t = 0

                   and d2.check_count > 0   

           group by D2.contract_no,D2.arc_receipt_detail_id,D2.paylist_code)  d3

on (t3.contract_no = d3.contract_no and  t3.arc_receipt_detail_id = d3.arc_receipt_detail_id

and t3.paylist_code = d3.paylist_code)

   when matched then 

   update set t3.dossier_count_t =d3.MCOUNT;

执行计划如下:b0f52c33a365ccc14e380f8b57da5ca2.png

嗯 999没了,传值也没了,无需二次计算了,让我们看看结果:

f7c96d1763d40e6e1561c7d6ac9f6a4f.png

结果很不错。当场解决。

快刀斩乱麻,什么表结构,索引选择性,老司机不用看这么细。速度解决问题才是王道,嘿嘿。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值