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 相关文章推荐
数据库的高级查询六:表连接查询:外连接(左外连接,右外连接,UNION关键字,连接中ON与WHERE的不同)
Apr 05 MySQL
Mysql效率优化定位较低sql的两种方式
May 26 MySQL
解决Navicat for MySQL 连接 MySQL 报2005错误的问题
May 29 MySQL
MySQL 时间类型的选择
Jun 05 MySQL
MySQL 那些常见的错误设计规范,你都知道吗
Jul 16 MySQL
MySQL千万级数据表的优化实战记录
Aug 04 MySQL
MySQL命令无法输入中文问题的解决方式
Aug 30 MySQL
mysql函数全面总结
Nov 11 MySQL
MySQL 开窗函数
Feb 15 MySQL
MySQL如何使备份得数据保持一致
May 02 MySQL
MySql中的json_extract函数处理json字段详情
Jun 05 MySQL
MySQL约束(创建表时的各种条件说明)
Jun 21 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
手把手教你使用DedeCms的采集的图文教程
2007/03/11 PHP
MySQL修改密码方法总结
2008/03/25 PHP
mac下Apache + MySql + PHP搭建网站开发环境
2014/06/02 PHP
smarty中js的调用方法示例
2014/10/27 PHP
php输出形式实例整理
2020/05/05 PHP
JS将制定内容复制到剪切板示例代码
2014/02/11 Javascript
jquery attr方法获取input的checked属性问题
2014/05/26 Javascript
JQuery替换DOM节点的方法
2015/06/11 Javascript
jQuery匹配文档链接并添加class的方法
2015/06/26 Javascript
js实现将选中值累加到文本框的方法
2015/08/12 Javascript
jquery选择器简述
2015/08/31 Javascript
AngularJS上拉加载问题解决方法
2016/05/23 Javascript
jQuery获取所有父级元素及同级元素及子元素的方法(推荐)
2018/01/21 jQuery
vue.js通过路由实现经典的三栏布局实例代码
2018/07/08 Javascript
详解vue服务端渲染浏览器端缓存(keep-alive)
2018/10/12 Javascript
移动端图片上传旋转、压缩问题的方法
2018/10/16 Javascript
Vue动态路由缓存不相互影响的解决办法
2019/02/19 Javascript
JavaScript 处理树数据结构的方法示例
2019/06/16 Javascript
node.JS的crypto加密模块使用方法详解(MD5,AES,Hmac,Diffie-Hellman加密)
2020/02/06 Javascript
[03:17]2014DOTA2 国际邀请赛中国区预选赛 四强专访
2014/05/23 DOTA
python根据开头和结尾字符串获取中间字符串的方法
2015/03/26 Python
让python 3支持mysqldb的解决方法
2017/02/14 Python
python简单线程和协程学习心得(分享)
2017/06/14 Python
解决python 输出是省略号的问题
2018/04/19 Python
在pycharm中为项目导入anacodna环境的操作方法
2020/02/12 Python
Python爬虫抓取论坛关键字过程解析
2020/10/19 Python
Pytorch实验常用代码段汇总
2020/11/19 Python
Pottery Barn阿联酋:购买家具、家居装饰及更多
2019/12/08 全球购物
早晨薰衣草在线女性精品店:Morning Lavender
2021/01/04 全球购物
介绍一下grep命令的使用
2015/06/12 面试题
商务英语本科生的自我评价分享
2013/11/15 职场文书
技术总监个人的自我评价范文
2013/12/18 职场文书
爱情保证书
2015/01/17 职场文书
如何书写民事调解协议书?
2019/06/25 职场文书
Oracle创建只读账号的详细步骤
2021/06/07 Oracle
Windows Server 2012 R2服务器安装与配置的完整步骤
2022/07/15 Servers