MySQL中IN关键字与EXIST关键字的比较

ops/2025/3/13 17:21:09/

文章目录

      • **功能等价性分析**
        • **执行计划分析**:
      • **1. `EXISTS` 的工作原理**
        • **步骤拆解**:
      • **2. 为什么需要“利用索引快速定位”?**
        • **索引作用示例**:
      • **3. 与 `IN` 子查询的对比**
        • **`IN` 的工作方式**:
        • **关键差异**:
      • **4. 性能优化核心**
      • **5. 实际案例验证**
        • **场景**:
        • **执行计划分析**:
        • **结果**:
      • **6. 总结**
        • **效率总结**:
      • **5. 最终建议**

问题:
首先说明下面两句MYSQL语句实现的功能是否一样,接着比较它们的执行效率:

mysql">SELECT * from tableA where tableA.id in (select id from tableB)
mysql">SELECT * FROM tableA 
WHERE EXISTS (SELECT 1 FROM tableB WHERE tableA.id = tableB.id);

答:当 id 无 NULL 值且唯一时,两者功能一致。另外,第二句中的子查询使用 SELECT 1(最佳实践,无需实际列值)。通常情况下(id是主键,tableA 和 tableB 数据量较大,id 字段有索引。),第二句效率更高。


功能等价性分析

场景IN 的行为EXISTS 的行为
子查询无 NULL等价等价
子查询含 NULLtableA.id IN (1, NULL) 等价于 tableA.id=1 OR tableA.id=NULL,最终只有 id=1 的行匹配EXISTS 只要子查询有匹配(即使含 NULL)就会返回 TRUE

结论

  • tableB.id NULL 值且唯一时,两者功能一致。
  • tableB.idNULL 或重复值时,结果可能不同。

执行计划分析
方法优化策略适用场景
IN 子查询MySQL 可能将子查询物化为临时表,再通过 JOIN 或半连接优化。子查询结果集较小时效率高。
EXISTStableA 的每一行触发一次关联子查询,利用索引快速定位。tableA 较小且 tableB.id 有索引时效率高。

1. EXISTS 的工作原理

EXISTS 是一种 关联子查询(Correlated Subquery),其核心逻辑是:
对于外层查询(tableA)的每一行,触发一次内层子查询(tableB)的检查
具体流程如下:

步骤拆解
  1. 遍历外层表(tableA
    逐行读取 tableA 的数据,取当前行的 id 值(例如 id=100)。

  2. 执行子查询(tableB
    将外层 tableA.id=100 传入子查询,检查 tableB 中是否存在匹配的 id

    SELECT 1 FROM tableB WHERE id = 100;  -- 当前外层行的 id 值
    
  3. 判断结果

    • 若子查询返回至少一行结果 → EXISTSTRUE → 保留当前外层行。
    • 若子查询无结果 → EXISTSFALSE → 丢弃当前外层行。
  4. 循环处理
    重复上述过程,直到 tableA 所有行处理完毕。


2. 为什么需要“利用索引快速定位”?

在上述流程中,子查询 SELECT 1 FROM tableB WHERE id=100 需要快速判断 id=100 是否存在。
tableB.id 没有索引

  • 数据库需对 tableB 进行全表扫描 → 时间复杂度为 O(N),性能极差(尤其当 tableB 数据量大时)。

tableB.id 有索引(如主键索引或普通索引):

  • 数据库通过索引(如 B+Tree)直接定位到 id=100 → 时间复杂度为 O(logN),效率极高。
索引作用示例
  • 假设 tableB 有 100 万行数据:
    • 无索引:每次子查询需扫描 100 万行 → 总成本:1,000,000(外层行数) × 1,000,000(内层扫描) → 不可接受。
    • 有索引:每次子查询仅需 3~4 次磁盘 I/O(B+Tree 高度) → 总成本:1,000,000(外层行数) × 4(索引查询) → 高效。

3. 与 IN 子查询的对比

IN 的工作方式
SELECT * FROM tableA WHERE id IN (SELECT id FROM tableB);
  1. 执行子查询
    先执行 SELECT id FROM tableB,生成一个临时结果集(如 [1, 2, 3])。

  2. 遍历外层表(tableA
    逐行检查 tableA.id 是否在临时结果集中。

关键差异
特性EXISTSIN
子查询执行次数外层表行数(N次)1次
临时表物化无需物化需要物化子查询结果到临时表
索引依赖依赖内层表(tableB)的索引依赖外层表(tableA)的索引
NULL 值处理不受子查询中 NULL 影响IN 遇到 NULL 可能导致结果异常

4. 性能优化核心

  • EXISTS 高效的核心条件

    • 内层表(tableB)的关联字段(id)必须有索引。
    • 外层表(tableA)的数据量不宜过大(否则逐行触发子查询的总成本仍可能较高)。
  • IN 高效的核心条件

    • 子查询结果集较小,且外层表(tableA)的 id 字段有索引。

5. 实际案例验证

场景
  • tableA:10,000 行,id 无索引
  • tableB:1,000,000 行,id 有唯一索引
执行计划分析
  1. EXISTS 查询

    • tableA 的 10,000 行逐行触发子查询。
    • 每次子查询通过索引在 tableB 中快速定位 → 总成本 ≈ 10,000 × 4 I/O = 40,000 I/O。
  2. IN 查询

    • 先执行 SELECT id FROM tableB,生成 1,000,000 行的临时表。
    • tableA 的 10,000 行逐行在临时表中搜索 → 总成本 ≈ 1,000,000(物化) + 10,000 × 1,000,000(全扫描) → 性能灾难。
结果
  • EXISTS 明显优于 IN,尤其在子查询结果集大且内层表有索引时。

6. 总结

  • EXISTS 的本质:通过外层表驱动循环 + 内层索引快速定位,避免全表扫描。
  • 何时选择 EXISTS
    • 内层表(子查询表)的关联字段有索引。
    • 外层表数据量适中,或内层表数据量远大于外层表。
  • 验证方法
    EXPLAIN SELECT * FROM tableA 
    WHERE EXISTS (SELECT 1 FROM tableB WHERE tableA.id = tableB.id);
    
    检查执行计划中是否出现 Using index(表示索引生效)。
效率总结
  1. EXISTS 通常更高效
    • 避免物化临时表。
    • 通过索引快速判断是否存在匹配。
  2. IN 可能更高效的情况
    • 子查询结果集非常小且无索引。
    • 优化器将 IN 转换为 JOIN 并应用哈希/排序优化。

5. 最终建议

  • 优先使用 EXISTS:语义更清晰,且通常性能更优。
  • 强制功能一致性:若需严格匹配 IN 的行为(处理 NULL),可添加过滤条件:
    SELECT * FROM tableA 
    WHERE EXISTS (SELECT 1 FROM tableB WHERE tableA.id = tableB.id AND tableB.id IS NOT NULL  -- 显式排除 NULL 值
    );
    

http://www.ppmy.cn/ops/165459.html

相关文章

Hack Me Please: 1靶场渗透测试

Hack Me Please: 1 来自 <https://www.vulnhub.com/entry/hack-me-please-1,731/> 1&#xff0c;将两台虚拟机网络连接都改为NAT模式 2&#xff0c;攻击机上做namp局域网扫描发现靶机 nmap -sn 192.168.23.0/24 那么攻击机IP为192.168.23.182&#xff0c;靶场IP192.168.…

Linux_17进程控制

前提回顾&#xff1a; 页表可以将无序的物理地址映射为有序的; 通过进程地址空间&#xff0c;避免将内存直接暴漏给操作系统&#xff1b; cr3寄存器存放的有当前运行进程的页表的物理地址&#xff1b; 一、查看命令行参数和环境变量的地址 因为命令行参数和环境变量都是字符…

[从零开始学习JAVA] 新版本idea的数据库图形化界面

前言: 在看黑马javaweb的时候&#xff0c;发现视频中的版本是老版本,而我的是新版本 为了记录新版本的数据库界面图形化操作我打算写下这篇博客 案例 创建tb_user表 对应的结构如下 要求 1.id 是一行数据的唯一标识 2.username 用户名字段是非空且唯一的 3.name 姓名字…

Javascript ajax

9.1 学习ajax的前置知识——JSON JSON是什么 JSON(JavaScript Object Notation)是⼀种轻量级的数据交换格式&#xff0c;它基于JavaScript的⼀个⼦集&#xff0c;易于⼈的编写和阅读&#xff0c;也易于机器解析。 JSON采⽤完全独⽴于语⾔的⽂本格式&#xff0c;但是也使⽤了类似…

宇树人形机器人开源模型

1. 下载源码 https://github.com/unitreerobotics/unitree_ros.git2. 启动Gazebo roslaunch h1_description gazebo.launch3. 仿真效果 H1 GO2 B2 Laikago Z1 4. VMware: vmw_ioctl_command error Invalid argument 这个错误通常出现在虚拟机环境中运行需要OpenGL支持的应用…

iOS UICollectionViewCell 点击事件自动化埋点

iOS 中经常要进行埋点&#xff0c;我们这里支持 UICollectionViewCell. 进行自动化埋点&#xff0c;思路&#xff1a; 通过hook UICollectionViewCell 的setSelected:方法&#xff0c; 则新的方法中执行埋点逻辑&#xff0c;并调用原来的方法 直接上代码 implementation UICol…

Rust规律归纳随笔

1. 针对所有权规则&#xff1a; 唯一所有权 <------------> 智能指针(引用计数)<-------------->共享所有权 (引用计数) { 单线程&#xff1a;Rc 多线程&#xff1a;Arc } 2. 针对借用规则&#xff1a; 共享不可变&#xff08;多读&#xff09;<----->…

聊聊langchain4j的AiServicesAutoConfig

序 本文主要研究一下langchain4j-spring-boot-starter的AiServicesAutoConfig LangChain4jAutoConfig dev/langchain4j/spring/LangChain4jAutoConfig.java AutoConfiguration Import({AiServicesAutoConfig.class,RagAutoConfig.class,AiServiceScannerProcessor.class })…