title: 坑爹的MySql update in subquery tags:

  • mysql
  • update
  • in
  • 子查询
  • subquery categories: mysql date: 2017-10-28 16:31:16

背景

最近收到邮件如上,在做allot时发生db超时【前一段时间由于系统负载过大忽略了相关慢sql】

收到如上邮件考虑可能是allot详情太多导致发生超时 经过调查后发现事实并非如此

即使是一个材料某些情况也会发生超时

因此定位了相关代码 一段有趣的代码引起了注意

分析

关于超时在代码层次至少存在如下两个

  1. 事务超时
  2. mysql查询超时

目前mysql查询超时默认情况为30s【后期将逐渐降低】

事务超时 目前基于mybatis做了事务设置查询超时

@Overridepublic Object intercept(Invocation invocation) throws Throwable {Statement stmt = (Statement) invocation.getArgs()[0];Collection<Object> values = TransactionSynchronizationManager.getResourceMap().values();if (values.size() > 0) {for (Object obj : values) {if (obj != null && obj instanceof ConnectionHolder) {ConnectionHolder holder = (ConnectionHolder) obj;if (holder.hasTimeout()) {int queryTimeOut = holder.getTimeToLiveInSeconds();if (stmt.getQueryTimeout() != 0) {queryTimeOut = queryTimeOut < stmt.getQueryTimeout() ? queryTimeOut : stmt.getQueryTimeout();}stmt.setQueryTimeout(queryTimeOut);}break;}}}return invocation.proceed();}
复制代码

因此考虑该超时是默认30s超时了还是因为事务设置使得查询时间变成某个较小的时间导致呢?

经过调查后发现url基本在30s以上 那么就应该是事务超时了么?

当然这是有可能的!

观察了几封超时邮件后 发现每次超时均出现在同一个地方

直接上代码

<update id="editPartInfoPrice" parameterType="com.air.tqb.model.TmPartInfo">UPDATE tm_part_info_detailSETmodifier = #{modifier}<if test="purchasePrice!=null">,purchase_price=#{purchasePrice}</if><if test="sellPrice!=null">,sell_price=#{sellPrice}</if><if test="transferPrice!=null">,transfer_price=#{transferPrice}</if>,modifiedtime = now()WHEREpk_id=CAST(#{pkId} as unsigned );<include refid="updateChainPrice"/></update><sql id="updateChainPrice"><if test="(chain band @com.air.tqb.model.enums.PartInfoChain@PURCHASE.value) eq @com.air.tqb.model.enums.PartInfoChain@PURCHASE.value and purchasePrice!=null">update tm_part_info_detail SET purchase_price=#{purchasePrice} where info_id in(select info_id from (SELECT info_id FROM tm_part_info_detail WHERE pk_id = #{pkId}) AS temp);</if><if test="(chain band @com.air.tqb.model.enums.PartInfoChain@SELL.value)  eq @com.air.tqb.model.enums.PartInfoChain@SELL.value and sellPrice!=null">update tm_part_info_detail SET sell_price=#{sellPrice} where info_id in(select info_id from (SELECT info_id FROM tm_part_info_detail WHERE pk_id = #{pkId}) AS temp);</if><if test="(chain band @com.air.tqb.model.enums.PartInfoChain@TRANSFER.value)  eq @com.air.tqb.model.enums.PartInfoChain@TRANSFER.value and transferPrice!=null">update tm_part_info_detail SET transfer_price=#{transferPrice} where info_id in(select info_id from (SELECT info_id FROM tm_part_info_detail WHERE pk_id = #{pkId}) AS temp);</if>
复制代码

sql很简单 并且相关地方都是加了索引 看起来都不像是慢的原因。

于是手动执行了该sql

    update  tm_part_info_detail set sell_price=1 where info_id in(select info_id from (SELECT info_id FROM tm_part_info_detail WHERE pk_id ='3398651') AS temp)
复制代码

WTF!耗时10s 这么简单的sql为啥执行花这么久呢?

那看来还是单条sql超时导致!

来查看一下 执行计划

    explain update  tm_part_info_detail set sell_price=1 where info_id in(select info_id from (SELECT info_id FROM tm_part_info_detail WHERE pk_id ='3398651') AS temp);
复制代码

某些sql客户端不支持查询update语句的执行计划 简单方案就是加上explain 然后执行sql

看来确实相当耗时的操作啊!

我们再来看一看select的执行计划

    explain select * from  tm_part_info_detail  where info_id in(select info_id from (SELECT info_id FROM tm_part_info_detail WHERE pk_id ='3398651') AS temp);
复制代码

数据相差实在太大!

来看一下mysql官方的解释

13.2.10.7 Correlated Subqueries

correlated subquery is a subquery that contains a reference to a table that also appears in the outer query. For example:

SELECT * FROM t1WHERE column1 = ANY (SELECT column1 FROM t2WHERE t2.column2 = t1.column2);
复制代码

Notice that the subquery contains a reference to a column of t1, even though the subquery's FROM clause does not mention a table t1. So, MySQL looks outside the subquery, and finds t1 in the outer query.

Suppose that table t1 contains a row where column1 = 5 and column2 = 6; meanwhile, table t2 contains a row where column1 = 5 and column2 = 7. The simple expression ... WHERE column1 = ANY (SELECT column1 FROM t2) would be TRUE, but in this example, the WHERE clause within the subquery is FALSE (because (5,6) is not equal to (5,7)), so the expression as a whole is FALSE.

Scoping rule: MySQL evaluates from inside to outside. For example:

SELECT column1 FROM t1 AS xWHERE x.column1 = (SELECT column1 FROM t2 AS xWHERE x.column1 = (SELECT column1 FROM t3WHERE x.column2 = t3.column1));
复制代码

In this statement, x.column2 must be a column in table t2 because SELECT column1 FROM t2 AS x ... renames t2. It is not a column in table t1 because SELECT column1 FROM t1 ... is an outer query that is farther out.

For subqueries in HAVING or ORDER BY clauses, MySQL also looks for column names in the outer select list.

For certain cases, a correlated subquery is optimized. For example:

val IN (SELECT key_val FROM tbl_name WHERE correlated_condition)
复制代码

Otherwise, they are inefficient and likely to be slow. Rewriting the query as a join might improve performance.

Aggregate functions in correlated subqueries may contain outer references, provided the function contains nothing but outer references, and provided the function is not contained in another function or expression.

很明显 优化器也是戴有色眼镜看sql的啊……基本相同的sql对于update和select走了不同的执行计划【优化器只能保证尽量快】update就是上文所说的inefficient and likely to be slow

解决方案

因此方案也很简单 update该语句改成inner join 即可

    UPDATE tm_part_info_detail d1INNER JOIN tm_part_info_detail d2 ON d1.info_id = d2.info_idSET d1.sell_price = 1WHEREd2.pk_id = '3398651';
复制代码

坑爹的MySql update in subquery相关推荐

  1. mysql update中使用subquery

    首先,在mysql workbench内执行update时,缺省是使用安全更新模式的,如果在update sql内的where没有指定id等主键条件,会告警并推出执行.你可以关闭安全模式,执行如下语句 ...

  2. mysql update w3c_PHP MySQL Update

    PHP MySQL Update 对于 MySQL 数据库中的数据你可以根据需要进行更新! UPDATE 语句用于中修改数据库表中的数据. 更新数据库中的数据 UPDATE 语句用于更新数据库表中已存 ...

  3. mysql set 子表,mysql update set 更新表数据

    1.update ...set...where... 题目:修改students id=2的name为"yanxia" mysql>update  students  set ...

  4. PHP MySQL Update

    PHP MySQL Update UPDATE 语句用于中修改数据库表中的数据. 更新数据库中的数据 UPDATE 语句用于更新数据库表中已存在的记录. 语法 UPDATE table_name SE ...

  5. mysql菜鸟教程update_PHP MySQL Update

    PHP MySQL Update UPDATE 语句用于中修改数据库表中的数据. 更新数据库中的数据 UPDATE 语句用于更新数据库表中已存在的记录. 语法 UPDATE table_name SE ...

  6. mysql update delete_MySQL中UPDATE与DELETE语句的使用教程

    UPDATE 更新UPDATE SET 语法用于修改更新数据表中的数据. 语法: UPDATE tb_name SET column1 = new_value1,column2 = new_value ...

  7. MySQL UPDATE 语句一个“经典”的坑

    转载自  MySQL UPDATE 语句一个"经典"的坑 来源:ju.outofmemory.cn/entry/336774 有问题的SQL语句 why? 倒回去再重试验一把 最近 ...

  8. mysql update 联合更新_Mysql update多表联合更新的方法小结

    下面我建两个表,并执行一系列sql语句,仔细观察sql执行后表中数据的变化,很容易就能理解多表联合更新的用法 student表 class表 1. 执行 UPDATE student s , clas ...

  9. mysql update修改数据_MySQL UPDATE:修改数据(更新数据)

    在 MySQL 中,可以使用 UPDATE 语句来修改.更新一个或多个表的数据. UPDATE 语句的基本语法 使用 UPDATE 语句修改单个表,语法格式为: UPDATE SET 字段 1=值 1 ...

最新文章

  1. 以前写的一点东西,放上来吧。否则就扔掉了
  2. 8 种常见的SQL错误用法
  3. 从门禁系统的使用体验看良好的交互设计原则
  4. CSDN:因博主近期注重写专栏文章(已超过150篇),订阅博主专栏人数在突增,近期很有可能提高专栏价格(已订阅的不受影响),提前声明,敬请理解!
  5. 【CodeForces - 270A】Fancy Fence (几何,思维,水题)
  6. 云服务器主机内网 ip 和外网 ip 的区别
  7. mysql类似的数据库_MemSQL学习笔记-类似MySQL的数据库
  8. 移动语音引擎相关开发笔记
  9. 没有富士康?外媒称苹果新款iPhone SE由和硕独家组装
  10. Unity中使用多构造函数(转)
  11. 三星android11推送,三星将在2020年1月开始推送Android 10系统:等太久
  12. 如何在 webpack 项目中使用绝对路径
  13. bat复制文件到指定目录同名_利用bat让文件在指定时间自动进行备份
  14. python实现诺基亚双人贪吃蛇小游戏
  15. 超级搜索术-读书笔记
  16. 转. Dynamics AX 20年简史
  17. window.performance(监控网页与程序性能)
  18. 在Fcitx5中使用自定义双拼方案
  19. 如何用excel筛选相似内容_如何excel中筛选两个表中相同的数据
  20. Android输入事件从读取到分发三:InputDispatcherThread线程分发事件的过程

热门文章

  1. 基于SpringCloud + Oauth2.0 + ShiroRedis + JWT + Gateway + Nacos + Nginx + Vue实现的SaaS数字商城系统
  2. 1.4CAD2017绘图基础
  3. matlab 画金字塔,图像拼接中 高斯金字塔的建立 matlab程序详细解释 现金奖励
  4. 红底证件照背景怎么弄?试试这几种方法非常简单
  5. Python精讲:在Python中遍历字典的三大方法详解
  6. 【转】web移动端一些常用知识
  7. H5+Css3学习内容
  8. 决策树基础—比特化Bits,信息熵,条件熵,联合熵
  9. Could not find installable ISAM
  10. 【003】垃圾回收机制