MySQL中IN和EXISTS的用法

46 篇文章 2 订阅
订阅专栏

exists对外表用loop逐条查询,每次查询都会查看exists的条件语句,当 exists里的条件语句能够返回记录行时(无论记录行是的多少,只要能返回),条件就为真,返回当前loop到的这条记录,反之如果exists里的条 件语句不能返回记录行,则当前loop到的这条记录被丢弃,exists的条件就像一个bool条件,当能返回结果集则为true,不能返回结果集则为 false

如下:

select * from user where exists (select 1);

对user表的记录逐条取出,由于子条件中的select 1永远能返回记录行,那么user表的所有记录都将被加入结果集,所以与 select * from user;是一样的

又如下

select * from user where exists (select * from user where userId = 0);

可以知道对user表进行loop时,检查条件语句(select * from user where userId = 0),由于userId永远不为0,所以条件语句永远返回空集,条件永远为false,那么user表的所有记录都将被丢弃

not exists与exists相反,也就是当exists条件有结果集返回时,loop到的记录将被丢弃,否则将loop到的记录加入结果集

总的来说,如果A表有n条记录,那么exists查询就是将这n条记录逐条取出,然后判断n遍exists条件 

in查询相当于多个or条件的叠加,这个比较好理解,比如下面的查询

select * from user where userId in (1, 2, 3);

等效于

select * from user where userId = 1 or userId = 2 or userId = 3;

not in与in相反,如下

select * from user where userId not in (1, 2, 3);

等效于

select * from user where userId != 1 and userId != 2 and userId != 3;

总的来说,in查询就是先将子查询条件的记录全都查出来,假设结果集为B,共有m条记录,然后在将子查询条件的结果集分解成m个,再进行m次查询

值得一提的是,in查询的子条件返回结果必须只有一个字段,例如

select * from user where userId in (select id from B);

而不能是

select * from user where userId in (select id, age from B);

而exists就没有这个限制

下面来考虑exists和in的性能

考虑如下SQL语句

1: select * from A where exists (select * from B where B.id = A.id);

2: select * from A where A.id in (select id from B);

查询1.可以转化以下伪代码,便于理解

for ($i = 0; $i < count(A); $i++) {

  $a = get_record(A, $i); #从A表逐条获取记录

  if (B.id = $a[id]) #如果子条件成立

    $result[] = $a;

}

return $result;

大概就是这么个意思,其实可以看到,查询1主要是用到了B表的索引,A表如何对查询的效率影响应该不大

假设B表的所有id为1,2,3,查询2可以转换为

select * from A where A.id = 1 or A.id = 2 or A.id = 3;

这个好理解了,这里主要是用到了A的索引,B表如何对查询影响不大

下面再看not exists 和 not in

1. select * from A where not exists (select * from B where B.id = A.id);

2. select * from A where A.id not in (select id from B);

看查询1,还是和上面一样,用了B的索引

而对于查询2,可以转化成如下语句

select * from A where A.id != 1 and A.id != 2 and A.id != 3;

可以知道not in是个范围查询,这种!=的范围查询无法使用任何索引,等于说A表的每条记录,都要在B表里遍历一次,查看B表里是否存在这条记录

故not exists比not in效率高

mysql中的in语句是把外表和内表作hash 连接,而exists语句是对外表作loop循环,每次loop循环再对内表进行查询。一直大家都认为exists比in语句的效率要高,这种说法其实是不准确的。这个是要区分环境的。
 

如果查询的两个表大小相当,那么用in和exists差别不大。 

如果两个表中一个较小,一个是大表,则子查询表大的用exists,子查询表小的用in: 

例如:表A(小表),表B(大表)

1:

select * from A where cc in (select cc from B) 效率低,用到了A表上cc列的索引;

select * from A where exists(select cc from B where cc=A.cc) 效率高,用到了B表上cc列的索引。 

相反的

2:

select * from B where cc in (select cc from A) 效率高,用到了B表上cc列的索引;

select * from B where exists(select cc from A where cc=B.cc) 效率低,用到了A表上cc列的索引。

not in 和not exists如果查询语句使用了not in 那么内外表都进行全表扫描,没有用到索引;而not extsts 的子查询依然能用到表上的索引。所以无论那个表大,用not exists都比not in要快。 

in 与 =的区别 

select name from student where name in ('zhang','wang','li','zhao'); 

与 

select name from student where name='zhang' or name='li' or name='wang' or name='zhao' 

的结果是相同的。

场景1:当IN中的取值只有一个主键时

315ce9a3b2ebb9b793673dd6a9405e78.png

我们只需要注意一个最重要的type 的信息很明显的提现是否用到索引:

type结果值从好到坏依次是:

system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL

all:全表扫描

index:另一种形式的全表扫描,只不过他的扫描方式是按照索引的顺序

range:有范围的索引扫描,相对于index的全表扫描,他有范围限制,因此要优于index

ref: 查找条件列使用了索引而且不为主键和unique。其实,意思就是虽然使用了索引,但该索引列的值并不唯一,有重复。这样即使使用索引快速查找到了第一条数据,仍然不能停止,要进行目标值附近的小范围扫描。但它的好处是它并不需要扫全表,因为索引是有序的,即便有重复值,也是在一个非常小的范围内扫描。

const:通常情况下,如果将一个主键放置到where后面作为条件查询,mysql优化器就能把这次查询优化转化为一个常量。至于如何转化以及何时转化,这个取决于优化器

一般来说,得保证查询至少达到range级别,最好能达到ref,type出现index和all时,表示走的是全表扫描没有走索引,效率低下,这时需要对sql进行调优。

当extra出现Using filesor或Using temproary时,表示无法使用索引,必须尽快做优化。

possible_keys:sql所用到的索引

key:显示MySQL实际决定使用的键(索引)。如果没有选择索引,键是NULL

rows: 显示MySQL认为它执行查询时必须检查的行数。

场景2:扩大IN中的取值范围

ec4ee9ea8d2ed345d3c804e6c89b2ecd.png

此时仍然走了索引,但是效率降低了

场景3:继续扩大IN的取值范围

15b5b909bd0f4f032e790f9b6f5eaf7a.png

看上面的图,发现此时已经没有走索引了,而是全表扫描。


在说一下结论

结论:IN肯定会走索引,但是当IN的取值范围较大时会导致索引失效,走全表扫描。

By the way:如果使用了 not in,则不走索引。

对于in的优化可以使用join进行代替

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执行计划走一波,

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

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

为什么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看下:

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

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

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

explain delete a from account as a where a.name in (select name from old_account)
复制代码

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

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

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

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

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

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


作者:捡田螺的小男孩
链接:https://juejin.cn/post/7013127536972398606
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

MySQLIN和EXISTS语法改写(SQL优化案例)
清平乐的技术专栏
04-22 2995
一、IN和EXISTS说明 (1)IN和NOT IN常用于where表达式,其作用是查询某个范围内的数据。 (2)IN和NOT IN语句分别可以用EXISTS和NOTEXISTS 进行改写 (3)EXISTS某些情况下可以比IN提高运行效率; 而用NOT EXISTS都比NOT IN要快 (4)如果两个表一个较小,一个是大表, 则建议子查询表大的用exists,子查询表小的用in 【注意】具体用法可以参考博文《MySQLEXISTS用法》 二、示例 表A(小表),表B(大表) 示例一: -- I
MySQL exists 和in 详解及区别
09-09
MySQL的`EXISTS`和`IN`都是在SQL查询用来检查特定条件是否存在的子查询操作符,但它们的工作方式和适用场景有所不同。下面将详细解释这两个操作符的用法和区别。 ### `EXISTS`操作符 `EXISTS`主要用于判断子...
关于MySqlEXISTS 和 NOT EXISTS相关问题
qq_40727884的博客
02-23 375
彻底解决:关于MySqlEXISTS 和 NOT EXISTS相关问题
mysql 替换not in_MySQL 代替in/not in 的sql语句
weixin_32562973的博客
01-19 3602
1.in和existsin是把外表和内表作hash连接,而exists是对外表作loop循环,每次loop循环再对内表进行查询,一直以来认为exists比in效率高的说法是不准确的。如果查询的两个表大小相当,那么用in和exists差别不大;如果两个表一个较小一个较大,则子查询表大的用exists,子查询表小的用in。一般情况下,主表的数据要少,从表的数据要多。例:table a(小表) 、t...
MySQL 关键字 IN 与 EXISTS 的使用与区别
一只奋斗的猪
07-16 2009
MySQL 关键字 IN 与 EXISTS 的使用与区别:在选择使用 IN 还是 EXISTS 关键字时,需要根据具体的查询需求和数据情况进行考虑。如果只是简单的匹配值是否在列表,可以使用 IN。如果需要根据子查询的返回结果来决定外部查询的结果,或者需要处理大量数据,那么使用 EXISTS 可能更为适合。
mysql in 查询改成_mysql百万数据查询 用什么代替in,该如何处理
weixin_36300275的博客
01-18 800
展开全部mysql百万数据查询用exists 代替 in 是一个62616964757a686964616fe59b9ee7ad9431333365643661好的选择:select num from a where num in(select num from b)用下面的语句替换:select num from a where exists(select 1 from b where num=...
MySQL代替in之临时表
苦行僧
11-26 1490
如果我们正常的使用IN去查询 SELECT * FROM a JOIN b ON a.id = b.id WHERE b.tag_id IN (1,2,3,4,5,6) 这种因为in里面的参数是连续的,它很快 如果in里面的数据不是连续的, SELECT * FROM a JOIN b ON a.id = b.id WHERE b.tag_id IN (88,2,303,410,581,316) 这种数据量一起来,它会很慢 解决方法,将in里面的数据更改为临时表去关联查询 SELECT * .
MySQL的in,exists,not in,not exists查询过程对比及结论
12-14
MySQL优化之in,exists,not in,not exists的区别in与existsin查询过程结论:exists查询过程:结论:not in与not existsnot in查询过程:结论:not exists查询过程:结论: 首先我们使用两个用户表作为实例 insert ...
对比分析MySQL语句的IN 和Exists
09-09
MySQL,`IN` 和 `EXISTS` 都是用来在查询进行子查询比较的两个关键字,但它们的工作方式和性能表现有所不同。通常,关于它们的效率比较存在一定的误解,这需要根据具体情况进行分析。 首先,`IN` 会将外部表...
mysql not in、left join、IS NULL、NOT EXISTS 效率问题记录
12-15
MySQL的`NOT IN`, `LEFT JOIN`, `IS NULL`, 和 `NOT EXISTS` 是四种不同的SQL查询方式,它们在特定情况下可以实现相似的功能,但实际执行效率可能会有很大差异。本文主要探讨这四种方法在处理大数据量时的性能表现...
mysqlin查询效率低的替代方法_MySQL 数据库设计总结
weixin_39611031的博客
11-22 766
本文由云+社区发表作者:漆洪凯规则1:一般情况可以选择MyISAM存储引擎,如果需要事务支持必须使用InnoDB存储引擎。注意:MyISAM存储引擎 B-tree索引有一个很大的限制:参与一个索引的所有字段的长度之和不能超过1000字节。另外MyISAM数据和索引是分开,而InnoDB的数据存储是按聚簇(cluster)索引有序排列的,主键是默认的聚簇(cluster)索引,因此MyISAM虽然在...
mysql in 用exitst代替,使用 join 来替代not in 做查询
weixin_32427471的博客
03-17 1070
倔强的老铁提出的问题:因为 not in不走索引,所以不在不得已情况下,就不要使用not in下面使用 join 来替代not in 做查询select ID from A where ID not in (select ID from B)替换为select A.ID from A left join B on A.ID=B.ID and B.ID is null或者:select A.ID f...
mysqlin查询效率低的替代方法_无需“in”的SQL盲注
weixin_39545895的博客
11-22 468
为了锻炼安全技术,我在TetCTF上想寻找一些新奇的网络挑战,并注意到一个有趣的系统——“Secure System”。其挑战目标是制作一个和SQL盲注有关的payload,并且不使用:UNION … SELECTinformation_schema“in”和“or”等关键词尽管还有其他安全过滤,但以上关键词是最难克服的障碍。information_schema的替代方法我在网上搜索了一下从My...
mysql in查询效率真的低_MySqlin查询效率低的替代方法
weixin_42321075的博客
02-07 3535
在项目,有一个in查询效率很低,耗时大概10多秒,修改后为1秒左右,本来想造一组数据展现效果的,发现实际情况比较复杂,跟具体的关联数据类型、列是否有索引等相关,实际情况并不是某种查询就肯定比另一种查询效率高。在此不再费心思造数据,仅列出几种可能的查询方法,以备需要时尝试。1. in查询实现select * from productwhere id in (select rela_id from ...
mysql 单表替代in_mysql IN 可否使用 EXISTS 替代,求提示
weixin_35740814的博客
01-30 803
一开始的语句是这样的SELECTa.*, b. NAME AS store_nameFROMorder aLEFT JOIN store b ON a.store_id = b.idWHEREa.wid IN (100020109, 100020114, 100020112, 100020133, 100020131, 100020143, 100020145, 100020145, 100020...
mysql关于in/not in 查询替换
sunyiqiang的专栏
08-18 1189
sql 采用not in 或者in的话 一般效率都很低并且很慢 一般都采用别的方式替换 一种是用 not in exits(单词忘怎么写了) 一种是用左连接查询 今天我用的是左连接查询解决我的问题 如下 select a.name ,a.userid from uts_group a left join (select DISTINCT serialNu...
数据库优化技巧:not in及in语句的连接替代方案
Lvmy的个人博客
10-25 421
在编写SQL语句时,如果要实现一张表有而另一张表没有的数据库时,通常第一直觉的写法就是: [code="sql"] select * from table1 where table1.id not in (select id from table2); [/code] 这种方法虽然很直观,但是in及not in的写法经常会影响其执行的效率,对于大数据量时,这个原因经常是性能的瓶颈。...
MySqlin查询效率低的替代方法
热门推荐
WWF_HelloWorld
12-18 2万+
在项目,有一个in查询效率很低,耗时大概10多秒,修改后为1秒左右,本来想造一组数据展现效果的,发现实际情况比较复杂,跟具体的关联数据类型、列是否有索引等相关,实际情况并不是某种查询就肯定比另一种查询效率高。在此不再费心思造数据,仅列出几种可能的查询方法,以备需要时尝试。 1. in查询实现 select * from product where id in (select rela_id f...
基于SpringBoot+Vue+MySQL的养老院管理系统
最新发布
程序员大金的博客
09-20 599
基于SpringBoot+Vue+MySQL的养老院管理系统,附源码。
mysqlin和exists用法
07-28
MySQL,IN和EXISTS是两种常用的查询语句。它们的用法和功能有一些区别。 IN语句用于判断某个值是否存在于一个给定的列表。它的语法是: SELECT 列名 FROM 表名 WHERE 列名 IN (值1, 值2, ...); 而EXISTS语句...
写文章

热门文章

  • 动态规划详解 62338
  • 递归详解 34854
  • BIO,NIO,AIO区别 21232
  • 交叉熵 14393
  • notify()方法、notifyAll()方法和wait()方法 14345

分类专栏

  • 算法 102篇
  • redis 32篇
  • 设计模式 5篇
  • kafka 10篇
  • flowable 9篇
  • Java 149篇
  • nodejs 40篇
  • other 81篇
  • python 3篇
  • MySQL 46篇
  • 机器学习 33篇
  • 数据结构 5篇
  • mongodb 7篇
  • docker 2篇

最新评论

  • AtomicReference,AtomicStampedReference

    youxinren1111: 这文章有意思。要money就跟有意思。

  • mysql 批量插入

    ychai918: 如果批处理+分批,最后提交,这个性能怎么样呢

  • java中的 Future详解(以及ExecutorService中的各种方法)

    GA_ST: 收藏比点赞还多,你被白嫖了up表情包

  • java中的 Future详解(以及ExecutorService中的各种方法)

    GA_ST: 牛的牛的

  • 动态规划详解

    关于不上作者榜就原神启动那件事: 兄弟你好香,看一遍就懂了

最新文章

  • 2022.04.01 END
  • 索引下推
  • 索引长度
2022年27篇
2021年285篇
2020年35篇
2019年81篇
2018年87篇
2017年5篇

目录

目录

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值

玻璃钢生产厂家广东仿铜玻璃钢雕塑批发玻璃钢卡通牛雕塑定制蚌埠玻璃钢雕塑公司重庆市玻璃钢花盆厂家江苏周年庆典商场美陈价钱广东节庆商场美陈销售江苏商场户外美陈佛山定制玻璃钢人物雕塑周口抽象玻璃钢卡通雕塑厂家江苏拉丝玻璃钢雕塑销售价格温州定制玻璃钢人物雕塑玻璃钢古人物雕塑重庆玻璃钢泡沫雕塑武汉景观灯玻璃钢花盆玻璃钢牛雕塑摆件玻璃钢雕塑如何维护水果玻璃钢雕塑制作乾安玻璃钢雕塑厂家济南校园玻璃钢雕塑安装中山玻璃钢雕塑手工制作自贡玻璃钢雕塑摆件施工公司苏州商场美陈售价广场玻璃钢人物雕塑批发庆阳玻璃钢卡通雕塑定做人物玻璃钢雕塑供货商玻璃钢大卫雕塑安徽动物玻璃钢雕塑市场珠海玻璃钢雕塑材质佛山玻璃钢狗雕塑句容玻璃钢雕塑设计价格香港通过《维护国家安全条例》两大学生合买彩票中奖一人不认账让美丽中国“从细节出发”19岁小伙救下5人后溺亡 多方发声单亲妈妈陷入热恋 14岁儿子报警汪小菲曝离婚始末遭遇山火的松茸之乡雅江山火三名扑火人员牺牲系谣言何赛飞追着代拍打萧美琴窜访捷克 外交部回应卫健委通报少年有偿捐血浆16次猝死手机成瘾是影响睡眠质量重要因素高校汽车撞人致3死16伤 司机系学生315晚会后胖东来又人满为患了小米汽车超级工厂正式揭幕中国拥有亿元资产的家庭达13.3万户周杰伦一审败诉网易男孩8年未见母亲被告知被遗忘许家印被限制高消费饲养员用铁锨驱打大熊猫被辞退男子被猫抓伤后确诊“猫抓病”特朗普无法缴纳4.54亿美元罚金倪萍分享减重40斤方法联合利华开始重组张家界的山上“长”满了韩国人?张立群任西安交通大学校长杨倩无缘巴黎奥运“重生之我在北大当嫡校长”黑马情侣提车了专访95后高颜值猪保姆考生莫言也上北大硕士复试名单了网友洛杉矶偶遇贾玲专家建议不必谈骨泥色变沉迷短剧的人就像掉进了杀猪盘奥巴马现身唐宁街 黑色着装引猜测七年后宇文玥被薅头发捞上岸事业单位女子向同事水杯投不明物质凯特王妃现身!外出购物视频曝光河南驻马店通报西平中学跳楼事件王树国卸任西安交大校长 师生送别恒大被罚41.75亿到底怎么缴男子被流浪猫绊倒 投喂者赔24万房客欠租失踪 房东直发愁西双版纳热带植物园回应蜉蝣大爆发钱人豪晒法院裁定实锤抄袭外国人感慨凌晨的中国很安全胖东来员工每周单休无小长假白宫:哈马斯三号人物被杀测试车高速逃费 小米:已补缴老人退休金被冒领16年 金额超20万

玻璃钢生产厂家 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化