热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

警惕MySql更新sql的WHERE从句中的IN()子查询时出现的圈套_mysql

同样的写法,唯一不同的是一个update另一个select,差别咋就那么大呢?看来优化器并不总是那么靠谱的,它在这里就对update那条sql的

警惕 mysql 更新 sql 的 WHERE 从句中的 IN() 子查询时出现的陷阱
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 '流程发起者用户名',   `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
# [email protected]: 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 的执行计划:
警惕 MySql 更新 sql 的 WHERE 从句中的 IN() 子查询时出现的圈套_mysql
注意: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;

它的执行计划是:
警惕 MySql 更新 sql 的 WHERE 从句中的 IN() 子查询时出现的圈套_mysql
执行这个 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 的执行计划是:
警惕 MySql 更新 sql 的 WHERE 从句中的 IN() 子查询时出现的圈套_mysql

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

参考资料
  • https://dev.mysql.com/doc/refman/5.5/en/correlated-subqueries.html

版权声明:本文为博主原创文章,未经博主允许不得转载。

欢迎大家阅读《警惕 MySql 更新 sql 的 WHERE 从句中的 IN() 子查询时出现的圈套_mysql》,跪求各位点评,by



推荐阅读
  • mysql自动打开文件_让docker中的mysql启动时自动执行sql文件
    本文提要本文目的不仅仅是创建一个MySQL的镜像,而是在其基础上再实现启动过程中自动导入数据及数据库用户的权限设置,并且在新创建出来的容器里自动启动My ... [详细]
  • 基于PgpoolII的PostgreSQL集群安装与配置教程
    本文介绍了基于PgpoolII的PostgreSQL集群的安装与配置教程。Pgpool-II是一个位于PostgreSQL服务器和PostgreSQL数据库客户端之间的中间件,提供了连接池、复制、负载均衡、缓存、看门狗、限制链接等功能,可以用于搭建高可用的PostgreSQL集群。文章详细介绍了通过yum安装Pgpool-II的步骤,并提供了相关的官方参考地址。 ... [详细]
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • SpringBoot uri统一权限管理的实现方法及步骤详解
    本文详细介绍了SpringBoot中实现uri统一权限管理的方法,包括表结构定义、自动统计URI并自动删除脏数据、程序启动加载等步骤。通过该方法可以提高系统的安全性,实现对系统任意接口的权限拦截验证。 ... [详细]
  • 本文介绍了Oracle数据库中tnsnames.ora文件的作用和配置方法。tnsnames.ora文件在数据库启动过程中会被读取,用于解析LOCAL_LISTENER,并且与侦听无关。文章还提供了配置LOCAL_LISTENER和1522端口的示例,并展示了listener.ora文件的内容。 ... [详细]
  • 本文详细介绍了Spring的JdbcTemplate的使用方法,包括执行存储过程、存储函数的call()方法,执行任何SQL语句的execute()方法,单个更新和批量更新的update()和batchUpdate()方法,以及单查和列表查询的query()和queryForXXX()方法。提供了经过测试的API供使用。 ... [详细]
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
  • 高质量SQL书写的30条建议
    本文提供了30条关于优化SQL的建议,包括避免使用select *,使用具体字段,以及使用limit 1等。这些建议是基于实际开发经验总结出来的,旨在帮助读者优化SQL查询。 ... [详细]
  • mysql-cluster集群sql节点高可用keepalived的故障处理过程
    本文描述了mysql-cluster集群sql节点高可用keepalived的故障处理过程,包括故障发生时间、故障描述、故障分析等内容。根据keepalived的日志分析,发现bogus VRRP packet received on eth0 !!!等错误信息,进而导致vip地址失效,使得mysql-cluster的api无法访问。针对这个问题,本文提供了相应的解决方案。 ... [详细]
  • MyBatis多表查询与动态SQL使用
    本文介绍了MyBatis多表查询与动态SQL的使用方法,包括一对一查询和一对多查询。同时还介绍了动态SQL的使用,包括if标签、trim标签、where标签、set标签和foreach标签的用法。文章还提供了相关的配置信息和示例代码。 ... [详细]
  • FineReport平台数据分析图表显示部分系列接口的应用场景和实现思路
    本文介绍了FineReport平台数据分析图表显示部分系列接口的应用场景和实现思路。当图表系列较多时,用户希望可以自己设置哪些系列显示,哪些系列不显示。通过调用FR.Chart.WebUtils.getChart("chartID").getChartWithIndex(chartIndex).setSeriesVisible()接口,可以获取需要显示的系列图表对象,并在表单中显示这些系列。本文以决策报表为例,详细介绍了实现方法,并给出了示例。 ... [详细]
  • Windows7 64位系统安装PLSQL Developer的步骤和注意事项
    本文介绍了在Windows7 64位系统上安装PLSQL Developer的步骤和注意事项。首先下载并安装PLSQL Developer,注意不要安装在默认目录下。然后下载Windows 32位的oracle instant client,并解压到指定路径。最后,按照自己的喜好对解压后的文件进行命名和压缩。 ... [详细]
  • Gitlab接入公司内部单点登录的安装和配置教程
    本文介绍了如何将公司内部的Gitlab系统接入单点登录服务,并提供了安装和配置的详细教程。通过使用oauth2协议,将原有的各子系统的独立登录统一迁移至单点登录。文章包括Gitlab的安装环境、版本号、编辑配置文件的步骤,并解决了在迁移过程中可能遇到的问题。 ... [详细]
  • 本文介绍了解决java开源项目apache commons email简单使用报错的方法,包括使用正确的JAR包和正确的代码配置,以及相关参数的设置。详细介绍了如何使用apache commons email发送邮件。 ... [详细]
  • 浅谈EditText控件的inputType类型
    其中大多数是用不到的,这里总结一下常用的几种键盘效果1、numberDecimal(可以带小数点的浮点格式)只可以输入0-9数字和小数点,即只浮点数2、number(数字格式 )只 ... [详细]
author-avatar
-_-小欢欢-_-
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有