视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
对比分析MySQL语句中的IN和Exists
2020-11-09 20:25:40 责编:小采
文档


CREATE TABLE t_author (
id int(15) NOT NULL AUTO_INCREMENT,
author_id bigint(20) NOT NULL,</font>
author_name varchar(32) NOT NULL,
dynasty varchar(16) NOT NULL,
poetry_num int(8) NOT NULL DEFAULT '0'
PRIMARY KEY (id),
<font color=red>UNIQUE KEY authorid_idx (author_id) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=13339 DEFAULT CHARSET=utf8mb4

执行计划分析 IN 执行过程

sql示例:select * from tabA where tabA.x in (select x from tabB where y>0 );

其执行计划:
(1)执行tabB表的子查询,得到结果集B,可以使用到tabB表的索引y;
(2)执行tabA表的查询,查询条件是tabA.x在结果集B里面,可以使用到tabA表的索引x。

Exists执行过程

sql示例:select from tabA where exists (select from tabB where y>0);

其执行计划:

(1)先将tabA表所有记录取到。
(2)逐行针对tabA表的记录,去关联tabB表,判断tabB表的子查询是否有返回数据,5.5之后的版本使用Block Nested Loop(Block 嵌套循环)。
(3)如果子查询有返回数据,则将tabA当前记录返回到结果集。
tabA相当于取全表数据遍历,tabB可以使用到索引。

实验过程

实验针对相同结果集的IN和Exists 的SQL语句进行分析。
包含IN的SQL语句:

select from t_author ta where author_id in
(select author_id from t_poetry tp where tp.poetry_id>3650 );

包含Exists的SQL语句:

select from t_author ta where exists
(select * from t_poetry tp where tp.poetry_id>3650 and tp.author_id=ta.author_id);

第一次实验数据情况

t_author表,13355条记录;t_poetry表,子查询筛选结果集 where poetry_id>293650 ,121条记录;

执行结果

使用exists耗时0.94S, 使用in耗时0.03S,IN 效率高于Exists

原因分析

对t_poetry表的子查询结果集很小,且两者在t_poetry表都能使用索引,对t_poetry子查询的消耗基本一致。两者区别在于,使用 in 时,t_author表能使用索引:


使用exists时,t_author表全表扫描:


在子查询结果集较小时,查询耗时主要表现在对t_author表的遍历上。

第二次实验数据情况

t_author表,13355条记录;t_poetry表,子查询筛选结果集 where poetry_id>3650 ,28783记录;

执行时间

使用exists耗时0.12S, 使用in耗时0.48S,Exists效率高于 IN。

原因分析

两者的索引使用情况跟第一次实验是一致的,唯一区别是子查询筛选结果集的大小不同,但实验结果已经跟第一次的不同了。这种情况下子查询结果集很大,我们看看mysql的查询计划:
使用in时,由于子查询结果集很大,对t_author和t_poetry表都接近于全表扫描,此时对t_author表的遍历耗时差异对整体效率影响可以忽略,执行计划里多了一行<auto_key>,在接近全表扫描的情况下,mysql优化器选择了auto_key来遍历t_author表:

使用exists时,数据量的变化没有带来执行计划的改变,但由于子查询结果集很大,5.5以后的MySQL版本在exists匹配查询结果时使用的是Block Nested-Loop(Block嵌套循环,引入join buffer,类似于缓存功能)开始对查询效率产生显著影响,尤其针对<font color=red>子查询结果集很大</font>的情况下能显著改善查询匹配效率:

实验结论

根据上述两个实验及实验结果,我们可以较清晰的理解IN 和Exists的执行过程,并归纳出IN 和Exists的适用场景:

IN查询在内部表和外部表上都可以使用到索引; Exists查询仅在内部表上可以使用到索引;当子查询结果集很大,而外部表较小的时候,Exists的Block Nested Loop(Block 嵌套循环)的作用开始显现,并弥补外部表无法用到索引的缺陷,查询效率会优于IN。当子查询结果集较小,而外部表很大的时候,Exists的Block嵌套循环优化效果不明显,IN 的外表索引优势占主要作用,此时IN的查询效率会优于Exists。 网上的说法不准确。其实“表的规模”不是看内部表和外部表,而是外部表和子查询结果集。最后一点,也是最重要的一点:世间没有绝对的真理,掌握事物的本质,针对不同的场景进行实践验证才是最可靠有效的方法。 实验过程中发现的问题补充

仅对不同数据集情况下的上述exists语句分析时发现,数据集越大,消耗的时间反而变小,觉得很奇怪。
具体查询条件为:

where tp.poetry_id>3650,耗时0.13S
where tp.poetry_id>293650,耗时0.46S

可能原因:条件值大,查询越靠后,需要遍历的记录越多,造成最终消耗越多的时间。这个解释有待进一步验证后再补充。

您可能感兴趣的文章:

  • MySQL exists 和in 详解及区别
  • mySQL中in查询与exists查询的区别小结
  • MYSQL IN 与 EXISTS 的优化示例介绍
  • mysql not in、left join、IS NULL、NOT EXISTS 效率问题记录
  • 下载本文
    显示全文
    专题