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 相关文章推荐
MySQL官方导出工具mysqlpump的使用
May 21 MySQL
mysql5.7使用binlog 恢复数据的方法
Jun 03 MySQL
浅谈MySQL user权限表
Jun 18 MySQL
MySQL REVOKE实现删除用户权限
Jun 18 MySQL
MySQL之select、distinct、limit的使用
Nov 11 MySQL
浅谈MySql整型索引和字符串索引失效或隐式转换问题
Nov 20 MySQL
mysql timestamp比较查询遇到的坑及解决
Nov 27 MySQL
SQL基础查询和LINQ集成化查询
Jan 18 MySQL
Pycharm远程调试和MySQL数据库授权问题
Mar 18 MySQL
MySQL的存储函数与存储过程的区别解析
Apr 08 MySQL
MySQL事务操作的四大特性以及并发事务问题
Apr 12 MySQL
MySQL8.0 Undo Tablespace管理详解
Jun 16 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
非常不错的MySQL优化的8条经验
2008/03/24 PHP
PHP中__get()和__set()的用法实例详解
2013/06/04 PHP
PHP中判断文件存在使用is_file还是file_exists?
2015/04/03 PHP
PHP生成及获取JSON文件的方法
2016/08/23 PHP
php 生成签名及验证签名详解
2016/10/26 PHP
由php中字符offset特征造成的绕过漏洞详解
2017/07/07 PHP
Thinkphp5+plupload实现的图片上传功能示例【支持实时预览】
2019/05/08 PHP
JavaScript isPrototypeOf和hasOwnProperty使用区别
2010/03/04 Javascript
Js event事件在IE、FF兼容性问题
2011/01/01 Javascript
jQuery图片滚动图片的效果(另类实现)
2013/06/02 Javascript
jQuery实现鼠标移到元素上动态提示消息框效果
2013/10/20 Javascript
JSONObject使用方法详解
2015/12/17 Javascript
详谈for循环里面的break和continue语句
2017/07/20 Javascript
详解webpack import()动态加载模块踩坑
2018/07/17 Javascript
微信小程序wx:for循环的实例详解
2018/10/07 Javascript
手挽手带你学React之React-router4.x的使用
2019/02/14 Javascript
taro开发微信小程序的实践
2019/05/21 Javascript
Vue axios与Go Frame后端框架的Options请求跨域问题详解
2020/03/03 Javascript
Angular利用HTTP POST下载流文件的步骤记录
2020/07/26 Javascript
解决vue项目打包上服务器显示404错误,本地没出错的问题
2020/11/03 Javascript
利用QT写一个极简单的图形化Python闹钟程序
2015/04/07 Python
Python实现爬虫从网络上下载文档的实例代码
2018/06/13 Python
Python爬虫爬取新浪微博内容示例【基于代理IP】
2018/08/03 Python
Python 实现异步调用函数的示例讲解
2018/10/14 Python
wxpython+pymysql实现用户登陆功能
2019/11/19 Python
详解利用python识别图片中的条码(pyzbar)及条码图片矫正和增强
2020/11/17 Python
实例讲解CSS3中的border-radius属性
2015/08/18 HTML / CSS
编写类String的构造函数、析构函数和赋值函数
2012/05/29 面试题
传播学专业毕业生自荐信
2013/11/04 职场文书
体育教育专业自荐信范文
2013/12/20 职场文书
2015年度个人业务工作总结
2015/04/27 职场文书
乡镇法制宣传日活动总结
2015/05/05 职场文书
JavaScript 反射学习技巧
2021/10/16 Javascript
Spring事务管理下synchronized锁失效问题的解决方法
2022/03/31 Java/Android
golang使用map实现去除重复数组
2022/04/14 Golang
利用正则表达式匹配浮点型数据
2022/05/30 Java/Android