delete in子查询不走索引问题分析


Posted in MySQL onJuly 07, 2022

文章开篇前,先问大家一个问题:delete in子查询,是否会走索引呢?很多伙伴第一感觉就是:会走索引。最近我们有个生产问题,就跟它有关。本文将跟大家一起探讨这个问题,并附上优化方案。

delete in子查询不走索引问题分析

问题复现

MySQL版本是5.7,假设当前有两张表accountold_account,表结构如下:

CREATE TABLE `old_account` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键Id',
  `name` varchar(255) DEFAULT NULL COMMENT '账户名',
  `balance` int(11) DEFAULT NULL COMMENT '余额',
  `create_time` datetime NOT NULL COMMENT '创建时间',
  `update_time` datetime NOT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  KEY `idx_name` (`name`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1570068 DEFAULT CHARSET=utf8 ROW_FORMAT=REDUNDANT COMMENT='老的账户表';

CREATE TABLE `account` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键Id',
  `name` varchar(255) DEFAULT NULL COMMENT '账户名',
  `balance` int(11) DEFAULT NULL COMMENT '余额',
  `create_time` datetime NOT NULL COMMENT '创建时间',
  `update_time` datetime NOT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  KEY `idx_name` (`name`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1570068 DEFAULT CHARSET=utf8 ROW_FORMAT=REDUNDANT COMMENT='账户表';

执行的SQL如下:

delete from account where name in (select name from old_account);

我们explain执行计划走一波,

delete in子查询不走索引问题分析

explain结果可以发现:先全表扫描 account,然后逐行执行子查询判断条件是否满足;显然,这个执行计划和我们预期不符合,因为并没有走索引

但是如果把delete换成select,就会走索引。如下:

delete in子查询不走索引问题分析

为什么select in子查询会走索引,delete in子查询却不会走索引呢?

原因分析

select in子查询语句跟delete in子查询语句的不同点到底在哪里呢?

我们执行以下SQL看看

explain select * from account where name in (select name from old_account);
show WARNINGS;

show WARNINGS 可以查看优化后,最终执行的sql

结果如下:

select `test2`.`account`.`id` AS `id`,`test2`.`account`.`name` AS `name`,`test2`.`account`.`balance` AS `balance`,`test2`.`account`.`create_time` AS `create_time`,`test2`.`account`.`update_time` AS `update_time` from `test2`.`account` 
semi join (`test2`.`old_account`)
where (`test2`.`account`.`name` = `test2`.`old_account`.`name`)

可以发现,实际执行的时候,MySQL对select in子查询做了优化,把子查询改成join的方式,所以可以走索引。但是很遗憾,对于delete in子查询,MySQL却没有对它做这个优化。

优化方案

那如何优化这个问题呢?通过上面的分析,显然可以把delete in子查询改为join的方式。我们改为join的方式后,再explain看下:

delete in子查询不走索引问题分析

可以发现,改用join的方式是可以走索引的,完美解决了这个问题。

实际上,对于update或者delete子查询的语句,MySQL官网也是推荐join的方式优化

delete in子查询不走索引问题分析

其实呢,给表加别名,也可以解决这个问题哦,如下:

explain delete a from account as a where a.name in (select name from old_account)

delete in子查询不走索引问题分析

为什么加个别名就可以走索引了呢?

what?为啥加个别名,delete in子查询又行了,又走索引了?

我们回过头来看看explain的执行计划,可以发现Extra那一栏,有个LooseScan

delete in子查询不走索引问题分析

LooseScan是什么呢? 其实它是一种策略,是semi join子查询的一种执行策略。

因为子查询改为join,是可以让delete in子查询走索引;加别名呢,会走LooseScan策略,而LooseScan策略,本质上就是semi join子查询的一种执行策略。

因此,加别名就可以让delete in子查询走索引啦!

总结

本博文分析了delete in子查询不走索引的原因,并附上解决方案。delete in在日常开发,是非常常见的,平时大家工作中,需要注意一下。同时呢,建议大家工作的时候,写SQL的时候,尽量养成一个好习惯,先用explain分析一下SQL,更多关于delete in子查询索引的资料请关注三水点靠木其它相关文章!


Tags in this post...

MySQL 相关文章推荐
52条SQL语句教你性能优化
May 25 MySQL
MySQL之PXC集群搭建的方法步骤
May 25 MySQL
Mysql数据库索引面试题(程序员基础技能)
May 31 MySQL
浅谈MySQL之select优化方案
Aug 07 MySQL
MYSQL 的10大经典优化案例场景实战
Sep 14 MySQL
MySQL学习之基础操作总结
Mar 19 MySQL
mysql中如何用命令创建联合唯一索引
Apr 20 MySQL
mysql字段为NULL索引是否会失效实例详解
May 30 MySQL
MySQL范围查询优化的场景实例详解
Jun 10 MySQL
MySql如何将查询的出来的字段进行转换
Jun 14 MySQL
mysql拆分字符串作为查询条件的示例代码
Jul 07 MySQL
MySQL性能指标TPS+QPS+IOPS压测
Aug 05 MySQL
MySQL提升大量数据查询效率的优化神器
mysql查看表结构的三种方法总结
Jul 07 #MySQL
MySQL中正则表达式(REGEXP)使用详解
MySQL实现字段分割一行转多行的示例代码
MySQL控制流函数(-if ,elseif,else,case...when)
Jul 07 #MySQL
mysql拆分字符串作为查询条件的示例代码
Jul 07 #MySQL
mysql全面解析json/数组
Jul 07 #MySQL
You might like
PHP函数http_build_query使用详解
2014/08/20 PHP
PHP中一些可以替代正则表达式函数的字符串操作函数
2014/11/17 PHP
php结合md5的加密解密算法实例
2016/09/30 PHP
php str_replace替换指定次数的方法详解
2017/05/05 PHP
js 时间格式与时间戳的相互转换示例代码
2013/12/25 Javascript
js中reverse函数的用法详解
2013/12/26 Javascript
JS动态增加删除UL节点LI及相关内容示例
2014/05/21 Javascript
JavaScript实现的Tween算法及缓冲特效实例代码
2015/11/03 Javascript
前端程序员必须知道的高性能Javascript知识
2016/08/24 Javascript
JS获取IE版本号与HTML设置IE文档模式的方法
2016/10/09 Javascript
基于Javascript倒计时效果
2016/12/22 Javascript
详解AngularJS脏检查机制及$timeout的妙用
2017/06/19 Javascript
Node.js利用js-xlsx处理Excel文件的方法详解
2017/07/05 Javascript
页面缩放兼容性处理方法(zoom,Firefox火狐浏览器)
2017/08/29 Javascript
利用JS hash制作单页Web应用的方法详解
2017/10/10 Javascript
vue路由跳转时判断用户是否登录功能的实现
2017/10/26 Javascript
jQuery pagination分页示例详解
2018/10/23 jQuery
vue 导航菜单刷新状态不消失,显示对应的路由界面操作
2020/08/06 Javascript
qpython3 读取安卓lastpass Cookies
2016/06/19 Python
Python的collections模块中的OrderedDict有序字典
2016/07/07 Python
python实现的二叉树定义与遍历算法实例
2017/06/30 Python
python tornado微信开发入门代码
2018/08/24 Python
Python实现堡垒机模式下远程命令执行操作示例
2019/05/09 Python
解决pycharm每次打开项目都需要配置解释器和安装库问题
2020/02/26 Python
Python绘制词云图之可视化神器pyecharts的方法
2021/02/23 Python
可自定义箭头样式的CSS3气泡提示框
2016/03/16 HTML / CSS
英国现代家具和装饰网站:PN Home
2018/08/16 全球购物
美国运动鞋和服装网上商店:YCMC
2018/09/15 全球购物
来自南加州灵感的工作和娱乐服装:TravisMathew
2019/05/01 全球购物
Why do we need Unit test
2013/01/03 面试题
怎样写好自我评价呢?
2014/02/16 职场文书
2015年公司保安年终工作总结
2015/05/14 职场文书
海上钢琴师的观后感
2015/06/11 职场文书
Python Parser的用法
2021/05/12 Python
使用springMVC所需要的pom配置
2021/09/15 Java/Android
python在package下继续嵌套一个package
2022/04/14 Python