遇到性能问题的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. kali的更新源

    先安装的系统最好更新更新 apt-get update apt-get upgrade 因为默认会去国外的网站去下载....慢 为了速度 可以修改更新源 vim /etc/apt/sources.li ...

  2. 斯坦福CS231n—深度学习与计算机视觉----学习笔记 课时11

    课时11 神经网络训练细节part1(下) 2010年,Glorot等人写的论文,我们称之为Xavier初始化,他们关注了神经元的方差表达式.他们推荐一种初始化方式,那就是对每个神经元的输入进行开根号 ...

  3. (水题)Codeforces - 650A - Watchmen

    http://codeforces.com/contest/650/problem/A 一开始想了很久都没有考虑到重复点的影响,解欧拉距离和曼哈顿距离相等可以得到 $x_i=x_j$ 或 $y_i=y ...

  4. 51nod 1247 可能的路径(gcd)

    传送门 题意 略 分析 有以下结论 \(1.(x,y)->(y,x)\) \(2.(x,y)->(a,b)==>(a,b)->(x,y)\) 证明 做如下变换 \((a,b)- ...

  5. IT兄弟连 Java Web教程 经典面试题

    1.什么是B/S结构?什么是C/S结构? B/S是Browser/Server的缩写客户机上只要安装—个浏览器(Browser)加Netscape Navigator或Internet Explore ...

  6. hdu 3172 Virtual Friends (字符串的并查集)

    一开始一直wa,因为第一个数字t也是多组输入. 然后一直超时,因为我用的是C++里面的cin,所以非常耗时,几乎比scanf慢了10倍,但是加上了一个语句后: std::ios::sync_with_ ...

  7. iOS Swift3 用全局“宏”时要注意的问题

    当你需要定义一个APP全局“宏”来调用 UserDefaults.standard里存储的值的时候, 一定要将这个“宏”定义为计算属性,否则你得到的值只会在APP启动的时候计算一次. 示例如下: va ...

  8. UltraEdit - 怎么显示文件标签栏和侧边栏

    显示文件标签栏 view -> views/lists -> open Files Tabs 显示侧边栏 view -> views/lists -> File Tree Vi ...

  9. SQL - nulls值排序问题

    给字段排序时遇到的null值问题 当我们使用order by来为指定的字段进行排序时,如果db中该字段的值存在着null值,那么在排序时这些null值会不会参与排序呢?如果参与排序的话,又是以怎样的标 ...

  10. AtCoder Grand Contest 005 C - Tree Restoring

    题目传送门:https://agc005.contest.atcoder.jp/tasks/agc005_c 题目大意: 给定一个长度为\(N\)的整数序列\(A_i\),问能否构造一个\(N\)个节 ...