遇到性能问题的sql如下:
sql1:
UPDATE amlclientlevel a
SET    a.client_value = (SELECT l.client_value
                         FROM   amlclientdynamiclevel l
                         WHERE  l.inner_client_id = a.inner_client_id)
WHERE  a.client_value = 0
       AND a.need_value = '0'
       AND a.client_status = '0'
       AND EXISTS (SELECT 1
                   FROM   amlclientdynamiclevel l
                   WHERE  l.inner_client_id = a.inner_client_id);
此sql执行超过两个小时跑不出结果。两张表都是几百兆大小的小表,按照常理小表不应该产生性能问题。
 
执行计划1:
 

amlclientlevel 表上的索引

amlclientdynamiclevel 表上的索引
 

问题发生的原因有两点
 
第一点:上诉update的写法会导致amlclientlevel和amlclientdynamiclevel在两个地方产生关联,一处是EXISTS一处是set。
SET  a.client_value = (SELECT l.client_value
                         FROM   amlclientdynamiclevel l
                         WHERE  l.inner_client_id = a.inner_client_id)
这样的set写法执行的过程其实是类比标量子查询的执行过程。对应update的每一个返回行都需要。
 
小知识点:
8 - filter(NVL("L"."INNER_CLIENT_ID",' ')=:B1)
sql本身没有绑定变量,在执行计划中 出现绑定变量值,应该是执行计划中出现了传值。应该留意这样的情况,传值可能导致严重的性能问题。
 
 
第二点:生成index hash join VIEW 。本身这样的执行步骤效率不高,结合set 引用的类比标量子查询的更新方式,导致数万次的index hash join VIEW生成及过滤数据成了整个sql的瓶颈。
 
执行计划2:

 
解决思路其实也是两点
第一点:改善set写法让其不再发生类比标量子查询的执行过程,减少一个关联。改写update为merge into的写法。
sql2:
MERGE /*+  GATHER_PLAN_STATISTICS J1 */ INTO AMLCLIENTLEVEL_BAK A
   USING AMLCLIENTDYNAMICLEVEL_BAK L
                         ON (L.INNER_CLIENT_ID = A.INNER_CLIENT_ID)
WHEN MATCHED THEN
UPDATE  SET A.CLIENT_VALUE = L.CLIENT_VALUE
WHERE A.CLIENT_VALUE = 0
   AND A.NEED_VALUE = '0'
   AND A.CLIENT_STATUS = '0';
 
执行计划3:
 
优化后的执行效率为13s。上面的执行计划缺少有效的数据过滤后再进行两表关联。
 
尝试改写merge让其先进行数据过滤
sql3:
MERGE /*+ GATHER_PLAN_STATISTICS J1 */ INTO (select * from AMLCLIENTLEVEL_BAK A WHERE A.CLIENT_VALUE = 0
   AND A.NEED_VALUE = '0'
   AND A.CLIENT_STATUS = '0') A
   USING AMLCLIENTDYNAMICLEVEL_BAK L
                         ON (L.INNER_CLIENT_ID = A.INNER_CLIENT_ID)
WHEN MATCHED THEN
UPDATE  SET A.CLIENT_VALUE = L.CLIENT_VALUE;
本次改写未起到先进行数据过滤作用,oracle内部选取的执行计划依旧是执行计划3。
 
实际上在表上添加了两个有效的索引后,sql3的执行计划(执行计划4)才是先进行数据过滤,效率已经提高。
create index AMLCLIENTLEVEL_BAK_IDX01 on AMLCLIENTLEVEL_BAK (CLIENT_VALUE, NEED_VALUE);
create index AMLCLIENTDYNAMICLEVEL_B_IDX1 on AMLCLIENTDYNAMICLEVEL_BAK (INNER_CLIENT_ID, CLIENT_VALUE);
 
执行计划4:
 

小知识点:
ON (L.INNER_CLIENT_ID = A.INNER_CLIENT_ID and  A.CLIENT_VALUE = 0
   AND A.NEED_VALUE = '0'
   AND A.CLIENT_STATUS = '0')
SET中的字段不能出现在ON的关联条件中
 
第二点:优化index hash join VIEW,建立有效的索引,引导正确的sql的执行计划。
 
create index AMLCLIENTLEVEL_BAK_IDX01 on AMLCLIENTLEVEL_BAK (CLIENT_VALUE, NEED_VALUE);
create index AMLCLIENTDYNAMICLEVEL_B_IDX1 on AMLCLIENTDYNAMICLEVEL_BAK (INNER_CLIENT_ID, CLIENT_VALUE);
 
尝试建议索引不进行sql改写后的执行效率也是很高的。
执行计划5:

 
既然这样还有必要改写sql吗?
1、首先建立索引是比较危险的,可能导致其他sql出现性能问题,正确的方式是需要测试验证后决定。
2、针对不同业务系统数据处理情况决定,本次案例中数据量小,展示不出update和merge性能上的巨大差距。
3、如果数据量非常大,可能既需要sql改写又需要添加有效的索引。
 
 

一条update语句优化小记的更多相关文章

  1. sql执行万条update语句优化

    几个月没有更新笔记了,最近遇到一个坑爹的问题,顺道记录一下.. 需求是这样的:一次性修改上万条数据库. 项目是用MVC+linq的. 本来想着用 直接where() 1 var latentCusto ...

  2. 完蛋,公司被一条 update 语句干趴了!

    大家好,我是小林. 昨晚在群划水的时候,看到有位读者说了这么一件事. 在这里插入图片描述 大概就是,在线上执行一条 update 语句修改数据库数据的时候,where 条件没有带上索引,导致业务直接崩 ...

  3. 如何将多条update语句合并为一条

    需求: 如何将多条update语句合并为一条update语句:如,update table1 set col='2012' where id='2014001'      update table1  ...

  4. 一条update语句到底加了多少锁?带你深入理解底层原理

    迎面走来了你的面试官,身穿格子衫,挺着啤酒肚,发际线严重后移的中年男子. 手拿泡着枸杞的保温杯,胳膊夹着MacBook,MacBook上还贴着公司标语:"我爱加班". 面试开始,直 ...

  5. Oracle的update语句优化研究

    最近研究sql优化,以下文章转自互联网: 1.     语法 单表:UPDATE 表名称 SET 列名称 = 新值 WHERE 列名称 = 某值 如:update t_join_situation s ...

  6. MySQL45讲:一条update语句是怎样执行的

    首先创建一张表: create table T(ID int primary key,c int); 如果要更新ID=2这行+1:应该这样写 update T set c=c+1 where ID=2 ...

  7. [转]Oracle的update语句优化研究

    原文地址:http://blog.csdn.net/u011721927/article/details/39228001 一.         update语句的语法与原理 1.     语法 单表 ...

  8. Sql Server执行一条Update语句很慢,插入数据失败

    今天同事要我修改服务器数据库里面的2条数据,查看服务器上的SQL Server数据库的时候,发现这几天数据没有添加成功,然后发现磁盘很快就满了,执行Update语句时,执行半天都提示还在执行,查询语句 ...

  9. 用一条UPDATE语句交换两列的值

    在SQL UPDATE语句中,"="右侧的值在整个UPDATE语句中都是一致的,所有更新同时发生!因此以下语句将在没有临时变量的情况下交换两列的值: UPDATE table SE ...

随机推荐

  1. spark api之一:Spark官方文档 - 中文翻译

    转载请注明出处:http://www.cnblogs.com/BYRans/ 1 概述(Overview) 2 引入Spark(Linking with Spark) 3 初始化Spark(Initi ...

  2. mounted

    注意 mounted 不会承诺所有的子组件也都一起被挂载.如果你希望等到整个视图都渲染完毕,可以用 vm.$nextTick 替换掉 mounted: mounted: function () { t ...

  3. 常用的Ant风格书写

    原文地址 Ant风格,为请求路径的一种匹配方式 通配符 说明 ? 匹配任意一个字符 * 匹配任意字符(包括0个) **  匹配任意层路径(包括0个) 支持Ant风格 1.spring资源加载 clas ...

  4. B. Mishka and trip

    time limit per test 1 second memory limit per test 256 megabytes input standard input output standar ...

  5. Windows下允许redis远程访问

    1.安装很简单,下载安装包安装即可,略过: 下载msi的安装包,会自动安装进服务: 2.远程访问 Redis默认只允许本地访问,要使Redis可以远程访问,需修改配置文件: 我用的redis3.2 , ...

  6. Ubuntu中右击出现终端

    1 root用户 $sudo apt-get install  nautilus-open-terminal 2重启 3ok

  7. HDU 5879 Cure (数论)

    题意:给定n,求前 n 项 1/(k*k) 的和. 析:由于这个极限是 PI * PI / 6,所以我们可以找到分界点,然后计算就好. 代码如下: #pragma comment(linker, &q ...

  8. 基于 CODING 轻松搞定持续集成

    点击观看视频教程 带你一步一步搞定 CODING 持续集成 持续集成加速软件交付 持续集成这个概念是由 Grady Booch 在 1991 年首次提出,随后成为了 DevOps 的核心实践之一.持续 ...

  9. Luogu P2051[AHOI2009]中国象棋【dp】By cellur925

    题目传送门 题目大意:给定一个$n*m$的棋盘,求放三个“炮”使它们不共行也不共列的方案数.($n,m$$<=100$) 这题主要是转移比较困难,因为情况比较多,所以需要冷静大胆细心地进行分情况 ...

  10. 布线问题 最小生成树 prim + kruskal

    1 : 第一种 prime     首先确定一个点 作为已经确定的集合 , 然后以这个点为中心 , 向没有被收录的点 , 找最短距离( 到已经确定的点 ) , 找一个已知长度的最小长度的 边 加到 s ...