mer_stage 表有 216423 条记录,DDL:
CREATE TABLE `mer_stage` (
`STAGE_ID` int(11) NOT NULL AUTO_INCREMENT,
`MER_ID` int(11) NOT NULL,
`MER_CODE` varchar(16) DEFAULT NULL,
`MER_NAME` varchar(80) NOT NULL,
`INS_CODE` varchar(16) NOT NULL,
`INS_NAME` varchar(64) DEFAULT NULL,
`AGENT_CODE` varchar(16) DEFAULT NULL,
`AGENT_NAME` varchar(64) DEFAULT NULL,
`BIG_CATEGORY_NAME` varchar(32) DEFAULT NULL,
`SUB_CATEGORY_CODE` char(4) DEFAULT NULL,
`SUB_CATEGORY_NAME` varchar(64) DEFAULT NULL,
`LICENSE_CODE` varchar(64) DEFAULT NULL,
`LICENSE_NAME` varchar(64) DEFAULT NULL,
`SHORT_NAME` varchar(25) DEFAULT NULL,
`MER_STATUS` tinyint(4) DEFAULT NULL,
`PROVINCE_NAME` varchar(16) DEFAULT NULL,
`CITY_CODE` char(4) DEFAULT NULL,
`CITY_NAME` varchar(12) DEFAULT NULL,
`REGISTER_ADDRESS` varchar(128) DEFAULT NULL,
`BIZ_ADDRESS` varchar(128) DEFAULT NULL,
`TAX_REGISTRATION` varchar(32) DEFAULT NULL,
`INSTITUTION` varchar(16) DEFAULT NULL,
`LEGAL_NAME` varchar(40) DEFAULT NULL,
`LEGAL_CARD` varchar(32) DEFAULT NULL,
`LEGAL_PHONE` varchar(16) DEFAULT NULL,
`BIZ_SCOPE` varchar(128) DEFAULT NULL,
`BIZ_CONTENT` varchar(64) DEFAULT NULL,
`BIZ_TIME` varchar(32) DEFAULT NULL,
`LICENSE_EXPIRED` varchar(16) DEFAULT NULL,
`AVG_SINGLE_TRADE` int(11) DEFAULT NULL,
`AVG_MONTH_TRADE` int(11) DEFAULT NULL,
`BIZ_PLACE_OWNER` varchar(64) DEFAULT NULL,
`REGISTERED_CAPITAL` decimal(11,0) DEFAULT NULL,
`PAID_IN_CAPITAL` int(11) DEFAULT NULL,
`BIZ_PERIOD` tinyint(4) DEFAULT NULL,
`BIZ_AREA` int(11) DEFAULT NULL,
`SETTLE_PERIOD` tinyint(4) DEFAULT NULL,
`DELAY_TIME` varchar(50) DEFAULT NULL,
`DELAY_TYPE` tinyint(4) DEFAULT '0',
`BANK_CODE` varchar(40) DEFAULT NULL,
`BRANCH_CODE` varchar(25) DEFAULT NULL,
`BRANCH_CODE_ONE` varchar(25) DEFAULT NULL,
`BRANCH_CODE_TWO` varchar(25) DEFAULT NULL,
`BRANCH_NAME` varchar(128) DEFAULT NULL,
`ACCOUNT_CODE` varchar(32) DEFAULT NULL,
`ACCOUNT_NAME` varchar(80) DEFAULT NULL,
`BRANCH_PROVINCE` varchar(32) DEFAULT NULL,
`BRANCH_CITY_CODE` varchar(10) DEFAULT NULL,
`BRANCH_CITY_NAME` varchar(50) DEFAULT NULL,
`SETTLE_CURRENCY` varchar(16) DEFAULT NULL,
`SETTLE_PARAM` char(1) DEFAULT NULL,
`CUP_TYPE` tinyint(4) NOT NULL DEFAULT '1',
`CUP_CD` varchar(6) DEFAULT NULL,
`CUP_NM` varchar(80) DEFAULT NULL,
`UPI_TYPE` tinyint(4) NOT NULL DEFAULT '1',
`UPI_CD` varchar(6) DEFAULT NULL,
`UPI_NM` varchar(80) DEFAULT NULL,
`VISA_EDC_FEE` double DEFAULT NULL,
`VISA_DCC_FEE` double DEFAULT NULL,
`MASTERCARD_EDC_FEE` double DEFAULT NULL,
`MASTERCARD_DCC_FEE` double DEFAULT NULL,
`JCB_EDC_FEE` double DEFAULT NULL,
`AE_EDC_FEE` double DEFAULT NULL,
`DC_EDC_FEE` double DEFAULT NULL,
`CONTACT_NAME` varchar(40) DEFAULT NULL,
`CONTACT_FIXED` varchar(32) DEFAULT NULL,
`CONTACT_MOBILE` varchar(32) DEFAULT NULL,
`CONTACT_FAX` varchar(32) DEFAULT NULL,
`CONTACT_EMAIL` varchar(80) DEFAULT NULL,
`CONTACT_ADDRESS` varchar(128) DEFAULT NULL,
`CONTACT_ZIP` varchar(8) DEFAULT NULL,
`biz_license` text COMMENT '营业执照',
`tax_register_cert` text COMMENT '税务登记证',
`ins_cert` text COMMENT '组织机构代码证',
`legal_id_card` text COMMENT '法人身份证',
`open_license` text COMMENT '开户许可证',
`auth_letter` text COMMENT '授权书',
`portal_photo` text COMMENT '门头照片',
`cashier_photo` text COMMENT '收银台照片',
`scene_photo` text COMMENT '经营场景照片',
`mer_agreement` text COMMENT '商户协议',
`other_qualification` text COMMENT '其它特殊资质',
`EXPECT_OPEN_TIME` datetime DEFAULT NULL,
`IN_OUT_FLAG` varchar(32) DEFAULT NULL,
`DCC_MODE` int(2) DEFAULT '0',
`SPECIAL_FLAG` tinyint(4) DEFAULT NULL,
`TRADING_CURRENCY` varchar(3) DEFAULT NULL,
`STATUS` int(11) DEFAULT '0',
`EDITABLE` tinyint(4) DEFAULT NULL,
`MER_SINGLE_LIMIT` decimal(30,5) DEFAULT NULL,
`MER_DAY_LIMIT` decimal(30,5) DEFAULT NULL,
`MER_NATION` varchar(3) DEFAULT NULL,
`ROUTE_SCHEME` varchar(13) DEFAULT NULL,
`CREATOR_ID` int(11) DEFAULT NULL,
`CREATOR_NAME` varchar(32) DEFAULT NULL,
`create_time` datetime NOT NULL COMMENT '记录创建时间',
`modify_time` datetime NOT NULL COMMENT '最好改动时间',
`TERM_CNT` int(11) DEFAULT NULL,
`DATA_SRC` tinyint(4) NOT NULL DEFAULT '1',
`CUP_CARD_PLAN` bit(1) DEFAULT NULL,
`UPI_CARD_PLAN` bit(1) DEFAULT NULL,
`RISK_DESC` varchar(50) DEFAULT NULL,
`IS_FLAG` char(1) DEFAULT NULL,
`ALP` decimal(22,3) DEFAULT NULL,
`WXP` decimal(22,3) DEFAULT NULL,
`dfs_edc_fee` decimal(22,3) DEFAULT NULL,
`prp_edc_fee` decimal(22,3) DEFAULT NULL,
`in_account_id_card` text COMMENT '入账人身份证',
`in_account_bank_card` text COMMENT '入账银行卡信息',
`ins_credit_card` text COMMENT '机构信用代码证',
`ins_store_photo` text COMMENT '仓库照片',
`lease_agreement` text COMMENT '租赁协议',
`sct` decimal(22,3) DEFAULT NULL COMMENT '扫码支付(支付宝、微信整合)',
`card_type` char(1) DEFAULT '1' COMMENT '法人证件类型(1:身份证,2:护照)',
PRIMARY KEY (`STAGE_ID`),
KEY `mer_stage_s_e_ms` (`STATUS`,`EDITABLE`,`MER_STATUS`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=216826 DEFAULT CHARSET=utf8;

proc 表有 6450 条记录,DDL:

CREATE TABLE `proc` (
`proc_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '流程id',
`proc_name` varchar(32) NOT NULL COMMENT '流程名称。如 新增商户全聚德审批流程',
`proc_type` tinyint(4) NOT NULL COMMENT '流程类型:1-新增商户,2-变更商户,3-新增终端',
`associated_id` int(11) NOT NULL COMMENT '流程关联的商户id或其它',
`node_id` tinyint(4) NOT NULL COMMENT '流程进行到哪个节点',
`associated_name` varchar(64) DEFAULT NULL COMMENT '流程关联的商户名称',
`proc_status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '流程状态:1-启动流程,2-进行中,3-已完毕',
`starter_id` int(11) NOT NULL COMMENT '流程发起者用户id',
`starter_name` varchar(32) NOT NULL COMMENT '流程发起者username',
`node_name` varchar(64) NOT NULL COMMENT '节点名称',
`next_id` tinyint(4) NOT NULL COMMENT '下一节点id',
`next_name` varchar(64) NOT NULL COMMENT '下一节点名称',
`create_time` datetime NOT NULL COMMENT '记录创建时间',
`ass_version` datetime NOT NULL COMMENT '关联版本',
`node_remark` varchar(255) DEFAULT NULL COMMENT '备注',
`modify_time` datetime DEFAULT NULL COMMENT '上一节点完毕时间',
`mer_id` int(11) NOT NULL,
PRIMARY KEY (`proc_id`),
KEY `proc_mer_id_index` (`mer_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=6451 DEFAULT CHARSET=utf8 COMMENT='流程';

关于这两张表的一个慢查询日志例如以下:
# Time: 150703 15:13:33
# User@Host: test[test] @ localhost [127.0.0.1]  Id:     1
# Query_time: 2.101248  Lock_time: 0.046034 Rows_sent: 0  Rows_examined: 865689
SET timestamp=1435907613;
update mer_stage set editable = 1 where stage_id in(
select associated_id from proc where proc_id in(6446 , 6447 , 6450));
日志中能够看出该 sql 的运行时间是 2.101 s。
我们来查看一下该 sql 的运行计划:

注意:select_type 里出现了 DEPENDENT SUBQUERY。
这意味着什么?——子查询取决于外面的查询。MySql 先运行外查询。内查询依据这个查询结果(如运行计划里所述,190102 rows)的每一条记录组成新的查询语句:

select associated_id from proc where proc_id in(6446 , 6447 , 6450) and associated_id = '外查询结果.stage_id';

这就是个坑。

我相信,每一个写出上面这样的 sql 的程序猿都不会想到 MySql 会对其这样运行,这是大家不想看到的结果。
怎么办?
Uncorrelated subqueries treated as DEPENDENT by MySQL 提出了相同的问题可是却没有给出解决方式。

MySql 官方给出的解决方式是:
If you have a slow 'correlated' subquery with IN, you can optimize it with a join to get around the bug described by Ryan and Stephen. After the optimization the execution time is no longer O(M×N).
于是我们的 update 语句改写为:

update mer_stage m join proc p on m.stage_id = p.associated_id set m.editable = 1
where p.proc_id =6446 or p.proc_id =6447 or p.proc_id =6450;

它的运行计划是:

运行这个 update。用时 0.047s。意料之中。搞定。
有趣的是。我们来做一个尝试。把该 update 改为 select:

select * from mer_stage where stage_id in (select associated_id from proc where proc_id in (6446 , 6447 , 6450));

它的运行时间是 0.053 s。毫秒级。
该 sql 的运行计划是:

相同的写法,唯一不同的是一个 update 还有一个 select。区别咋就那么大呢?看来优化器并不总是那么靠谱的,它在这里就对 update 那条 sql 的子查询优化的非常糟糕。

參考资料

警惕 MySql 更新 sql 的 WHERE 从句中的 IN() 子查询时出现的陷阱的更多相关文章

  1. 警惕 MySql 更新 sql 的 WHERE 从句中的 IN() 子查询时出现的性能陷阱

    警惕 MySql 更新 sql 的 WHERE 从句中的 IN() 子查询时出现的性能陷阱 以下文章来源:https://blog.csdn.net/defonds/article/details/4 ...

  2. Mysql常用sql语句(19)- in / exists 子查询

    测试必备的Mysql常用sql语句系列 https://www.cnblogs.com/poloyy/category/1683347.html 前言 子查询在我们查询方法中是比较常用的,通过子查询可 ...

  3. SQL进阶系列之6用关联子查询比较行与行

    写在前面 使用SQL对同一行数据进行列间的比较很简单,只需要在WHERE子句里写上比较条件就可以了,对于不同行数据进行列间比较需要使用自关联子查询. 增长.减少.维持现状 需要用到行间比较的经典场景是 ...

  4. MySQL在字段中使用select子查询

    前几天看别人的代码中看到在字段中使用select子查询的方法,第一次见这种写法,然后研究了一下,记录下来 大概的形式是这样的: select a .*,(select b.another_field ...

  5. MySQL 使用profile分析慢sql,group left join效率高于子查询

    MySQL 使用profile分析慢sql,group left join效率高于子查询 http://blog.csdn.net/mchdba/article/details/54380221 -- ...

  6. 当没有用 EXISTS 引入子查询时,在选择列表中只能指定一个表达式。

    当没有用 EXISTS 引入子查询时,在选择列表中只能指定一个表达式.比如 select * from T_Employee where FNumber not in ( select top 5*  ...

  7. SELECT中常用的子查询操作

    MySQL中的子查询 是在MySQL中经常使用到的一个操作,不仅仅是用在DQL语句中,在DDL语句.DML语句中也都会常用到子查询. 子查询的定义: 子查询是将一个查询语句嵌套在另一个查询语句中: 在 ...

  8. 《MySQL必知必会学习笔记》:子查询

    子查询 在開始了解子查询之前,首先做下准备工作,建立3个表, 一个是customers表,当中包含:客户名字.客户ID.客户Tel等. 一个是orders表,当中包含:订单号.客户ID.订单时间等. ...

  9. 使用sql查询mysql/oracle/sql server/gp数据库中指定表的字段信息(字段名/字段类型/字段长度/是否是主键/是否为空)

    1,根据数据库类型拼接不同URL /** * 根据类型不同拼接连接的URL * @param dbType 1:mysql.2:oracle.3:sql server.4:gp * @param ip ...

随机推荐

  1. spring4.1.3+springmvc+mybatis3.2.1整合

    注意:这里使用了mybatis3.2.1版本,刚开始用了3.4.1的版本,会报一个很奇怪的错(java.lang.AbstractMethodError: org.mybatis.spring.tra ...

  2. UVa1225 Digit Counting

    #include <stdio.h>#include <string.h> int main(){    int T, N, i, j;    int a[10];    sc ...

  3. 【Cocos2D-x 3.5实战】坦克大战(1)环境配置

    前言: 最近课比较少,空闲时间比较多,一有时间就东想西想,想着想着就突然想到做手机游戏(android)了,学习下CoCos2d.看了一些CoCos2D的相关文档和教程,觉得是时候实战了,但是苦于没有 ...

  4. codeforces 282E. Sausage Maximization Trie

    题目链接 给n个数, 让你找出一个前缀和一个后缀, 它们异或完以后最大, 前缀和后缀都是异或得来的, 可以为空, 为空时按0算.前缀后缀不可覆盖. 这题好神, 竟然是Trie树... 首先将所有数的异 ...

  5. IMP-00008: unrecognized statement in the export file: string的问题分析

    分类: Linux 上周需要将oracle10g中的某一个用户下的对象导入到oracle11g中去.用exp在10g的数据库服务器上导出的dump文件,再用imp在11g的数据库服务器上将dump文件 ...

  6. Nginx工作原理和优化、漏洞(转)

    查看安装了哪些模块命令: [root@RG-PowerCache-X xcache]# nginx/sbin/nginx -Vnginx version: nginx/1.2.3built by gc ...

  7. java面试复习 I

    1 多线程 在程序开发中只要是多线程肯定永远以实现Runnable接口为主,因为实现Runnable接口相比继承Thread类有如下好处: 避免点继承的局限,一个类可以继承多个接口. 适合于资源的共享 ...

  8. Node.mongoose

    简介 mongodb是一款面向文档的数据库,不是关系型数据库,新手熟悉mysql.sqlserver等数据库的人可能入手稍微困难些,需要转换一下思想,可以不需要有固定的存储模式,以文档模型为存储内容相 ...

  9. 先登录 在跳转到tabBar

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launc ...

  10. 关于CDC在非控件类中的使用

    在非CStatic的派生类中,由于进行图形的绘制的话,我们需要对该类传入一个CDC以便于绘画.这是因为非CStatic等控件类无法自己产生onPaint这类的消息,因此需要借传入的CDC进行回执,然后 ...