面试官:Mysql中EXISTS与IN的使用有哪些差异

news2024/12/30 2:19:25

在数据库查询优化中,查询效率直接关系到应用程序性能。其中,IN和EXISTS是两种常见的子查询操作符,广泛应用于SQL查询语句,但它们在执行效率上有所不同。

本文深入探讨IN和EXISTS的工作原理,以及在何种情境下选择更为合适。通过对这两种操作符的详细分析,揭示它们在实际应用中的优缺点,一起了解如何在数据库查询中灵活运用IN和EXISTS,以优化查询语句的执行。

IN与EXISTS基本概念与用法

IN子查询

在MySQL中,当使用IN子查询时,主查询(外表)中的每一行都会与子查询(内表)的结果集进行比较。先执行子查询生成一个临时表,然后主查询取出对应的字段值,系统会遍历子查询结果集,检查这个字段值是否存在于子查询结果集中。如果存在,则该行满足条件,会被加入到最终的查询结果中。例如:

SELECT * FROM t_order where customer_no in (SELECT customer_no FROM t_customer WHERE country = 'US');

在这个例子中,对于t_order表中的每一行,MySQL会查看t_customer表中是否存在与其customer_no相匹配的记录。如果t_customer表中有任何行的customer_not_order表中当前行的customer_no相同,那么这一行就会被包含在最终查询结果中。

IN子查询的效率通常在子查询结果集较小的情况下较高,因为它需要处理并可能缓存整个子查询结果。

EXISTS子查询

EXISTS子查询则是用于判断关联性,它并不关心子查询返回的具体数据值,而只关注是否存在匹配的行。对于主查询表中的每一行,执行内部的EXISTS子查询。当EXISTS子查询找到一行或多行符合WHERE条件的记录时,立即返回真(TRUE)。这个TRUE值会导致外层查询的那一行被纳入最终结果中,因为WHERE EXISTS条件为真。一旦EXISTS子查询找到匹配项,它就不需要继续查找剩余的记录了,即实现了所谓的“短路”或“早期终结”。例如:

SELECT * FROM t_order torder WHERE EXISTS(SELECT 1 FROM t_customer tcustomer WHERE tcustomer.customer_no = torder.customer_no AND tcustomer.country = 'US');

在这个例子中,只要t_customer表中存在至少一条记录,其customer_not_order表中的当前行customer_no相符,MySQL就认为EXISTS条件为真,并将当前的t_order表行作为结果返回。无论t_customer表有多少其他相关记录,都不再影响此条目是否被选中。
EXISTS在子查询表大但只需验证是否存在对应关系时更高效,它支持“短路”机制,一旦找到匹配项就结束子查询,不必遍历完整个子查询表。

结论

MySQL中的IN语句是把外表和内表作HASH连接,而EXISTS语句是对外表作LOOP循环,每次LOOP循环再对内表进行查询,单纯的理解EXISTSIN语句的效率要高的说法其实是不准确的,要区分情景:

  • 如果查询的两不表大小相当,那么用EXISTSIN差别不大。
  • 如果两个表中一个较小,一个是大表,则子查询表大的用EXISTS,子查询表小的用 IN

验证

下面我们来通过实际案例去验证数据量和索引对INEXISTS子查询性能的影响。
我们创建两张表:

-- t_order
DROP TABLE IF EXISTS `t_order`;  
CREATE TABLE `t_order`(  
 id                 bigint UNSIGNED AUTO_INCREMENT COMMENT '自增主键'  
        PRIMARY KEY,  
 `order_no` varchar(16) NOT NULL DEFAULT '' COMMENT '订单编号',  
 `customer_no`      varchar(16) NOT NULL DEFAULT '' COMMENT '客户编号',  
`create_time`      datetime        NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间'  
) ENGINE = InnoDB  
  AUTO_INCREMENT = 1  
  DEFAULT CHARSET = utf8mb4 COMMENT ='测试订单表';  

-- t_customer
DROP TABLE IF EXISTS `t_customer`;  
CREATE TABLE `t_customer`(  
     id                 bigint UNSIGNED AUTO_INCREMENT COMMENT '自增主键'  
        PRIMARY KEY,  
     `customer_no`      varchar(16) NOT NULL DEFAULT '' COMMENT '客户编号',  
    `create_time`      datetime        NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间'  
)ENGINE = InnoDB  
  AUTO_INCREMENT = 1  
  DEFAULT CHARSET = utf8mb4 COMMENT ='测试订单客户表';

我们通过Python脚本往t_order中插入100万条数据,t_customer中插入1万条数据。

案例sql:

SELECT * FROM t_order where customer_no in (SELECT customer_no FROM t_customer WHERE country = 'US');

SELECT * FROM t_order torder WHERE EXISTS(SELECT 1 FROM t_customer tcustomer WHERE tcustomer.customer_no = torder.customer_no AND tcustomer.country = 'US');

案例执行建立在没有加索引情况下进行。

IN小表,EXISTS小表

我们在执行上面两条sql时会发现IN查询的速度远远高于EXISTS

SELECT * FROM t_order where customer_no in (SELECT customer_no FROM t_customer WHERE country = 'US');

SELECT * FROM t_order torder WHERE EXISTS(SELECT 1 FROM t_customer tcustomer WHERE tcustomer.customer_no = torder.customer_no AND tcustomer.country = 'US');

我们先看两个sql的执行计划:
image.png
本案例中IN查询的SQL执行了近5秒。
image.png
本案例中EXISTS查询的SQL执行了超过5分钟。

从上述执行计划中,我们可以看到IN查询和EXISTS查询在没有索引的情况下都进行了全表扫描:

  • IN查询:

    • 主查询对t_order表进行了全表扫描(ALL),由于没有索引,MySQL需要遍历1005915行数据。
    • 子查询对t_customer表也进行了全表扫描(MATERIALIZED),查找国家为’US’的客户编号。该表大小较小,有1000行数据。
  • EXISTS查询:

    • 主查询同样对t_order表进行了全表扫描(ALL),同理,无索引导致效率较低。
    • 子查询对t_customer表进行了全表扫描(DEPENDENT SUBQUERY),并且根据WHERE条件过滤出与主查询关联的数据。

虽然两者都未使用索引,但根据执行计划中的rows值,IN查询的子查询涉及的数据量要远小于主查询涉及的数据量。具体来说,在IN查询中,子查询只需要处理1000行数据,并将结果用于筛选主查询中的1005915行数据。而在EXISTS查询中,子查询虽然只返回1.00(几乎为1)个匹配记录,但它需要针对每一行主查询的结果进行检查,总共要处理1005915次。

因此,在这种情况下,IN查询的效率高于EXISTS查询的原因主要是子查询数据集大小的不同以及子查询对主查询的影响程度。尽管两个查询都没有利用到索引优化,但在实际执行时,IN查询所需的计算量相对较小,故其性能优于EXISTS查询。

IN大表,EXISTS大表

我们再次变更一下sql,让子查询是大表,观察一下他们的执行情况。即sql:

SELECT * FROM t_customer WHERE customer_no IN (SELECT customer_no FROM t_order);

SELECT * FROM t_customer tcustomer WHERE EXISTS(SELECT 1 FROM  t_order torder WHERE tcustomer.customer_no = torder.customer_no);

我们再次查看sql的执行计划:
image.png
执行IN查询语句时花费2秒。

image.png
执行EXISTS查询花费0.25秒。

由此可以看出EXISTS查询的效率远高于IN查询,我们结合执行计划进行分析:

  • IN查询:
    • 主查询(t_customer)由于没有索引,MySQL需要对整个表进行全表扫描,涉及行数为1000行。
    • 子查询(t_order)同样进行了全表扫描,涉及行数为1005915行。虽然子查询的结果通过自动生成的临时键与主查询关联,并且对于每一个主查询中的customer_no,子查询都能很快找到对应的记录(rows值为1),但由于子查询的数据量巨大,所以整体查询效率不高。
  • EXISTS查询:
    • 主查询(t_customer)仍然进行了全表扫描,涉及行数为1000行。
    • 子查询(t_order)也是全表扫描,但关键在于它是“DEPENDENT SUBQUERY”,这意味着它会依赖于外部查询(即主查询)的每一行结果来决定是否执行。尽管子查询需要处理1005915行数据,但由于其是根据主查询的每一条customer_no逐个检查是否存在匹配项,因此当遇到第一条不满足条件的customer_no时,就可以立即停止对子查询中剩余行的处理。这导致了在实际执行过程中,可能只需要检查一部分t_order表的数据即可完成所有主查询记录的验证,从而提高了查询效率。

在这个案例中,因为主查询表(t_customer)较小,而子查询表(t_order)较大,EXISTS查询能够在较早阶段停止不必要的计算,使得整体查询效率优于IN查询。

数据量以及索引对IN与`EXISTS性能的影响

在MySQL中,INEXISTS子查询的性能很大程度上取决于内外表的数据量以及相关的索引设置。

数据量的影响
  • 对于IN子查询:当内表(子查询结果集)较小且数据能够被有效索引时,IN通常表现良好。如果内表很大,即使有索引,由于需要生成并存储完整的子查询结果集以供主查询进行比对,因此随着内表记录数的增长,性能会逐渐下降。

  • 对于EXISTS子查询:当外层主查询表较大,而内表虽大但匹配条件的行数较少时,EXISTS的优势更加明显。因为它仅需找到一个匹配项就可以立即结束内部循环,返回真值,无需遍历整个内表。当内表数据量巨大但能快速定位到满足条件的少数行时,EXISTS相比IN更高效。

索引的影响:
  • 对于IN子查询:如果IN子查询中的字段具有有效的索引,可以减少内表的全表扫描,转而通过索引查找,显著提高查询效率。尤其是覆盖索引(索引包含了查询所需的所有列),可以直接从索引中获取信息,避免回表操作。

  • 对于EXISTS子查询:对于EXISTS子查询,同样要求相关联的字段上有合适的索引。例如,在上面的例子中,若t_customercustomer_no字段有索引,那么在执行WHERE tcustomer.customer_no = torder.customer_no时,可以通过索引快速定位匹配记录,从而加速子查询的执行过程。

在决定使用IN还是EXISTS时,首先应考虑的是内外表的数据规模以及关联字段上的索引情况。若内表较小或子查询结果集易于通过索引优化,IN可能是更好的选择。若关注是否存在关联关系且内表虽大但能满足条件的行数有限,同时外层主查询表可能更大,则EXISTS可能提供更高的查询性能。

当然最佳实践是结合实际业务需求、数据分布特点以及数据库统计信息,通过分析SQL执行计划来确定最合适的查询策略,并根据实际情况调整表结构和索引设计。

本文已收录于我的个人博客:码农Academy的博客,专注分享Java技术干货,包括Java基础、Spring Boot、Spring Cloud、Mysql、Redis、Elasticsearch、中间件、架构设计、面试题、程序员攻略等。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1418243.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

玩转WEB接口之三续篇【HTTPS证书申请 - nginx验证】

文章目录 一, 概述二,nginx下载三,访问域名1. 做域名映射2. 运行nginx并通过域名访问 四,配置SSL证书1. 配置证书文件2. nginx 添加证书文件 五、运行并验证1. 测试、重新加载2. https访问 一, 概述 接上篇 玩转WEB接…

Qt应用软件【串口篇】串口通信

文章目录 1.串口概述2.串口传输数据的基本原理电信号的传输过程 3.串口的几个概念数据位(Data Bits)奇偶校验位(Parity Bit)停止位(Stop Bits)流控制(Flow Control)波特率&#xff0…

找到满意的北京软件外包公司

寻得一家满意的软件外包开发公司,需明确自身需求,细心调研,筛选比较,这样方能找到技术实力雄厚、服务贴心的合作伙伴,助力企业数字化转型之路。要找到一家满意的软件外包开发公司,需要遵循以下几个步骤&…

UDF学习(七)非稳态宏和对流宏及UDS_DIFFUCITY宏

非稳态宏和对流宏—FLUENT UDF-DEFINE_UDS_UNSTEADY宏 非稳态如何挂载 UDF_DEFINE_UDS_FLUX宏 对流项的宏,可以从help文件中直接用 FLUENT UDF-DEFINE_UDS_DIFFUCITY宏 定义了扩散系数 两个宏:DEFINE_ANISOTROPIC_DIFFUSITY宏和DEFINE_DIFFUSIVITY&a…

校园圈子论坛系统--APP小程序H5,前后端源码交付,支持二开!uniAPP+PHP书写!

随着移动互联网的快速发展,校园社交成为了大学生们日常生活中重要的一部分。为了方便校园内学生的交流和互动,校园社交小程序逐渐走入人们的视野。本文将探讨校园社交小程序的开发以及其带来的益处。 校园社交小程序的开发涉及许多技术和设计方面。首先&…

用友移动管理系统 DownloadServlet 任意文件读取漏洞

免责声明:文章来源互联网收集整理,请勿利用文章内的相关技术从事非法测试,由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失,均由使用者本人负责,所产生的一切不良后果与文章作者无关。该…

MySQL:三大日志(binlog、redolog、undolog)

再了解三个日志前我们先了解一下MySQL的两层架构: Server 层负责建立连接、分析和执行 SQL。MySQL 大多数的核心功能模块都在这实现,主要包括连接器,查询缓存、解析器、预处理器、优化器、执行器等。另外,所有的内置函数和所有跨…

雅特力AT32 Workbench图形化代码生成工具,简化嵌入式开发利器

嵌入式系统应用市场广泛,早已遍及日常生活,随着产品需求复杂度的提升,32位MCU开发难度也随之增加,如何降低开发成本,缩短开发周期,是所有嵌入式开发人员的共同课题。 面对市场竞争日益加剧的情形&#xff…

JavaWeb创建详细

这个是自己总结的onenote 笔记 , 欢迎各位探讨指正

Lucene 查询原理

Lucene 查询原理 - 知乎 前言 Lucene 是一个基于 Java 的全文信息检索工具包,目前主流的搜索系统Elasticsearch和solr都是基于lucene的索引和搜索能力进行。想要理解搜索系统的实现原理,就需要深入lucene这一层,看看lucene是如何存储需要检…

移动端设计规范 - 文字使用规范

这是一篇关于移动端产品界面设计时,文字大小的使用规范,前端人员如果能了解一点的话,在实际开发中和设计沟通时,节省沟通成本,也能提高设计落地开发时的还原度。 关于 在做移动端产品设计时,有时候使用文字…

【JavaScript基础入门】06 JavaScript 数据类型

JavaScript 数据类型 目录 JavaScript 数据类型1. 数据类型1.1 数据类型简介1.2 简单数据类型1.2.1 简单数据类型(基本数据类型)1.2.2 数字型 N u m b e r \color{red}{Number} Number1.2.3 字符串型 S t r i n g \color{red}{String} String1.2.4 布尔…

介绍TCP/IP

TCP/IP(传输控制协议/互联网协议)是一种用于数据通信的基本通信协议,它是互联网的基础。TCP/IP指的是一组规则和过程,它规定了如何在网络上发送和接收数据。这个协议族由两个主要部分组成:传输控制协议(TCP…

SpringBoot 源码解析 - 持续更新

开始 spring initilizer:根据依赖构建工具、springboot 版本等生成 Java 工程。手把手教你手写一个最简单的 Spring Boot Starter Starter 命名规则 Spring 官方定义的 Starter 通常命名遵循的格式为 spring-boot-starter-{name},例如 spring-boot-star…

蓝牙----蓝牙GAP层

蓝牙协议栈----GAP GAP的角色连接过程连接参数 GAP:通用访问配置协议层 gap的角色发现的模式与过程连接模式与过程安全模式与过程 CC2640R2F的GAP层抽象 GAP的角色 Broadcaster 广播电台 -不可连接的广播者。Observer 观察者 -扫描广播者但无法启动连接。Periphe…

QA-GNN: 使用语言模型和知识图谱的推理问答

Abstract 使用预训练语言模型(LMs)和知识图谱(KGs)的知识回答问题的问题涉及两个挑战:在给定的问答上下文(问题和答案选择)中,方法需要(i)从大型知识图谱中识…

React一学就会(4): 强化练习二

书接上回,是不是感觉已经有点入门了。不过别急,码哥我准备了很多干货,等我们把这些基本几个章节的学完,码哥带着你一起装逼一起飞。我不大可能只是带着你们入门,那不是我的风格。码哥会教你如何开发一个完整中后台。前…

知识增强系列 ERNIE: Enhanced Representation through Knowledge Integration,论文解读

论文全称:通过知识集成增强语义表达 1. motivation ERNIE 目的在于通过知识屏蔽策略增强语言表示,其中屏蔽策略包括实体级屏蔽(Entity-level strategy)和短语级屏蔽(Phrase-level strategy)。 entity-level 策略通常会掩盖由多个单词组成的实体; Phrase…

智能加湿器数据分析:预计2025年市场规模将达到164.18亿元

随着经济的发展和人民生活水平的提高,人们对生活质量和健康的要求愈来愈高。空气加湿器慢慢的走进家庭当中,预计2023年中国线上超声波加湿器零售额同比下降4.9%;线上纯净型加湿器零售额同比增长44.8%。随着社会科技的不断进步和居民消费水平的不断提高&a…