在 SQL 中,EXISTSNOT EXISTSIN 都是用于子查询的条件运算符,用于根据子查询的结果过滤主查询的行。它们之间的区别主要体现在工作方式、效率、对 NULL 值的处理以及适用场景上。

1. EXISTS 和 NOT EXISTS

  • 作用:
    • EXISTS: 检查子查询是否至少返回一行。如果子查询返回至少一行,则条件为 TRUE
    • NOT EXISTS: 检查子查询是否没有返回任何行。如果子查询返回零行,则条件为 TRUE
  • 工作方式 (关联子查询):
    • EXISTS/NOT EXISTS 通常与关联子查询一起使用。
    • 对于主查询的每一行,数据库引擎都会执行一次子查询。
    • 子查询的 WHERE 子句通常会引用主查询当前行的列值(这就是“关联”的含义)。
    • 数据库引擎一旦在子查询中找到一行匹配(对于 EXISTS) 或确认没有匹配(对于 NOT EXISTS),就会停止执行该次子查询,因为它只需要知道是否存在记录,不需要知道具体有多少条或是什么内容。
  • 效率:
    • 当子查询可能返回大量结果,但你只关心“是否存在”时,EXISTS/NOT EXISTS 通常效率更高
    • 原因在于它们利用了短路求值:找到第一个匹配项(EXISTS) 或确认没有匹配项(NOT EXISTS) 后立即停止扫描子查询的表。
    • 关联条件(主表列 = 子查询表列)通常能有效利用索引。
  • 对 NULL 的处理:
    • EXISTS/NOT EXISTS 只关心子查询是否返回行。
    • 子查询中的 NULL不影响判断。只要子查询能返回至少一行(即使该行所有列都是 NULL),EXISTS 就为 TRUE;只要子查询返回零行,NOT EXISTS 就为 TRUE
  • 语法:
    SELECT column1, column2, ...
    FROM table_name_main main
    WHERE EXISTS (SELECT 1 -- 常用 SELECT 1, 实际返回什么值不重要,重要的是是否有行FROM table_name_sub subWHERE sub.correlated_column = main.correlated_column -- 关联条件AND ... -- 其他子查询条件
    );SELECT column1, column2, ...
    FROM table_name_main main
    WHERE NOT EXISTS (SELECT 1FROM table_name_sub subWHERE sub.correlated_column = main.correlated_column -- 关联条件AND ... -- 其他子查询条件
    );
    
  • 适用场景:
    • 检查主表记录在相关表中是否有对应记录(存在性检查)。
    • 检查主表记录在相关表中是否没有对应记录(缺失性检查)。
    • 当子查询逻辑依赖于主查询的当前行时(必须使用关联子查询)。

示例:找出有订单的客户 (EXISTS)

SELECT CustomerID, CustomerName
FROM Customers c
WHERE EXISTS (SELECT 1FROM Orders oWHERE o.CustomerID = c.CustomerID -- 关联条件:订单的客户ID = 当前客户ID
);
  • Customers 表的每一行,检查 Orders 表中是否有 CustomerID 匹配的订单。只要有一条订单,该客户就会被选出。

示例:找出没有订单的客户 (NOT EXISTS)

SELECT CustomerID, CustomerName
FROM Customers c
WHERE NOT EXISTS (SELECT 1FROM Orders oWHERE o.CustomerID = c.CustomerID -- 关联条件
);
  • Customers 表的每一行,检查 Orders 表中是否有 CustomerID 匹配的订单。如果一条都没有,该客户就会被选出。

2. IN (和 NOT IN)

  • 作用:
    • IN: 检查主查询列的值是否等于子查询返回结果集中的任何一个值
    • NOT IN: 检查主查询列的值是否不等于子查询返回结果集中的所有值
  • 工作方式 (非关联子查询 - 通常):
    • IN/NOT IN 通常与非关联子查询一起使用(但也可以是关联的,效率可能变差)。
    • 数据库引擎会先完整执行一次子查询,生成一个包含所有结果的中间结果集(值列表)
    • 然后,主查询会检查每行的指定列值是否存在于 (IN) 或不存在于 (NOT IN) 这个预先计算好的中间结果集中。
    • 这个过程类似于 WHERE column = value1 OR column = value2 OR ... (IN) 或 WHERE column <> value1 AND column <> value2 AND ... (NOT IN)。
  • 效率:
    • 当子查询返回的结果集非常小时,IN 可能比较高效,尤其是主查询列有索引且列表值不多时。
    • 当子查询返回的结果集非常大时,IN 可能效率较低
      • 需要存储整个中间结果集(可能消耗内存/临时空间)。
      • 主查询需要对这个庞大的列表进行查找(哈希或排序查找可能比索引查找慢)。
    • 如果 IN 子查询是关联的,效率通常不如 EXISTS,因为它需要为每一行重新生成或查找那个中间结果集。
  • 对 NULL 的处理 (重要陷阱!):
    • IN: 如果子查询返回的结果集中包含 NULL,这本身不影响 IN 的判断。value IN (1, 2, NULL) 等价于 value = 1 OR value = 2 OR value = NULLvalue = NULL 的结果是 UNKNOWN,但只要有 value=1value=2TRUE,整个条件就是 TRUE。如果 value 既不是 1 也不是 2,结果是 UNKNOWN(按 FALSE 处理)。
    • NOT IN: 对 NULL 值非常敏感! value NOT IN (1, 2, NULL) 等价于 value <> 1 AND value <> 2 AND value <> NULLvalue <> NULL 的结果总是 UNKNOWN。根据逻辑运算规则,TRUE AND UNKNOWN = UNKNOWNFALSE AND UNKNOWN = FALSEUNKNOWN AND UNKNOWN = UNKNOWN。所以,只要子查询结果集中包含 NULL,整个 NOT IN 条件对于主查询的任何行都会计算为 UNKNOWN(被当作 FALSE 处理),导致没有行被返回!这是 NOT IN 的最大陷阱。
  • 语法:
    SELECT column1, column2, ...
    FROM table_name_main
    WHERE column_name_main [NOT] IN (SELECT single_column_name -- 子查询必须只返回一列FROM table_name_sub[WHERE ...] -- 子查询条件
    );
    
  • 适用场景:
    • 检查主查询列的值是否在一个明确的、较小的静态值列表中(如 WHERE Country IN ('USA', 'UK', 'Canada'))。
    • 检查主查询列的值是否在一个独立的、返回少量唯一值的子查询结果集中。
    • 当子查询逻辑不依赖于主查询的当前行时(非关联子查询)。

示例:找出在特定国家的客户 (IN)

SELECT CustomerID, CustomerName
FROM Customers
WHERE Country IN ('Germany', 'France', 'Spain'); -- 静态值列表
SELECT CustomerID, CustomerName
FROM Customers
WHERE Country IN (SELECT DISTINCT SupplierCountry -- 独立子查询,返回少量国家FROM SuppliersWHERE SupplierName LIKE '%Gourmet%'
);

示例:NOT IN 的 NULL 陷阱演示
假设 SubTable 有一列 some_col,其中包含一行 NULL

SELECT *
FROM MainTable
WHERE main_col NOT IN (SELECT some_col FROM SubTable);
  • 如果 SubTablesome_col 包含 NULL,那么无论 main_col 的值是什么,这个查询永远不会返回任何行。因为 main_col NOT IN (..., NULL) 总是计算为 UNKNOWN (FALSE)。

关键区别总结

特性EXISTS / NOT EXISTSIN / NOT IN
核心目的检查存在性 (是否有/没有匹配行)检查成员资格 (值是否在/不在列表中)
工作机制关联子查询为主。对主表每一行执行子查询,找到/找不到即停。非关联子查询为主。先执行子查询生成完整值列表,主查询在列表中查找。
效率倾向通常更高效 (尤其子查询大时),利用短路和关联索引。小列表高效,大列表可能低效 (需存储和查找大列表)。关联子查询效率差。
处理 NULL安全。只关心行是否存在,NULL 行不影响判断。IN 安全NULL 在子查询结果中不影响。 NOT IN 危险!子查询结果含 NULL 会导致无结果 (整个条件变 UNKNOWN)。
子查询列子查询 SELECT 列表内容无关紧要 (常用 SELECT 1)。子查询必须且只能返回一列
主要用途基于相关表的存在性/缺失性检查。与静态值列表或独立小结果集进行值比较。

选择建议

  1. 进行存在性/缺失性检查时 (如“有订单的客户”、“没订单的产品”):
    • 首选 EXISTS (存在) 或 NOT EXISTS (缺失)。效率通常更高,语义更清晰,且完全避免 NOT INNULL 陷阱。
  2. 与小的、静态的值列表比较时 (如 Country IN ('US', 'UK')):
    • 使用 IN 非常合适且直观。
  3. 与一个独立的、返回少量唯一值的子查询结果比较时:
    • 可以使用 IN
    • 如果子查询可能返回 NULL 并且你需要使用 NOT IN务必确保子查询结果集排除 NULL (例如 WHERE NOT IN (SELECT col FROM ... WHERE col IS NOT NULL)),或者直接改用 NOT EXISTS 更安全。
  4. 避免 NOT IN 用于子查询:
    • 强烈建议不要使用 NOT IN (SELECT ...),尤其是当子查询结果集来源表可能包含 NULL 值时。总是优先用 NOT EXISTS 替代 NOT IN 用于子查询场景。
  5. 关联子查询中的存在性检查:
    • 必须使用 EXISTS/NOT EXISTSIN 虽然语法上可以写成关联的 (如 WHERE col IN (SELECT ... WHERE correlated_condition)),但其执行计划通常不如 EXISTS 高效。

总结一句话:存在性检查就用 EXISTS/NOT EXISTS;和小列表或独立小结果集比较值就用 IN坚决避免用 NOT IN 检查子查询的结果,用 NOT EXISTS 代替。理解它们的工作机制和 NULL 陷阱对于写出正确高效的 SQL 至关重要。

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

GitHub 趋势日报 (2025年06月25日)

&#x1f4ca; 由 TrendForge 系统生成 | &#x1f310; https://trendforge.devlive.org/ &#x1f310; 本日报中的项目描述已自动翻译为中文 &#x1f4c8; 今日获星趋势图 今日获星趋势图 880 awesome 788 build-your-own-x 691 free-for-dev 427 best-of-ml-python 404 …

互联网大厂Java求职面试:Java虚拟线程实战

互联网大厂Java求职面试&#xff1a;Java虚拟线程实战 文章内容 开篇&#xff1a;技术总监与程序员郑薪苦的三轮对话 在一场紧张而严肃的Java工程师面试中&#xff0c;技术总监张工正对候选人郑薪苦进行深入提问。郑薪苦虽然性格幽默&#xff0c;但对技术有着扎实的理解。今天…

网络安全的两大威胁:XSS与CSRF攻击实例解析

在网络攻击中,XSS跨站脚本攻击(Cross Site Scripting)与CSRF跨站请求伪造攻击(Cross-Site Request Forgery)是两种常见的攻击方式,它们之间存在显著的区别。以下是对这两种攻击方式的详细比较: 一、攻击原理 XSS跨站脚本攻击 攻击者通过在Web页面中注入恶意脚本来实现攻…

如何一次性将 iPhone 中的联系人转移到 PC

许多重要的联系人都存储在您的 iPhone 上。为了保护关键信息&#xff0c;您可能需要将联系人从 iPhone 转移到 PC&#xff0c;这是一种有效的联系人备份方法。如果您在将 iPhone 联系人转移到电脑上遇到困难&#xff0c;现在可以从本文中学习 5 个有效的解决方案&#xff0c;然…

Spring Boot开启定时任务的三种方式 【@EnableScheduling注解,SchedulingConfigurer接口,Quartz 框架】

Spring Boot 开启定时任务的三种方式​ ​ ​ 在 Spring Boot 应用开发过程中&#xff0c;定时任务是十分常见的需求&#xff0c;比如定时清理日志文件、定期备份数据库数据、定时发送邮件提醒等。Spring Boot 提供了多种开启定时任务的方式&#xff0c;本文将详细介绍三种常见…

LLM 编码器 怎么实现语义相关的 Token 向量更贴近? mask训练:上下文存在 ;; 自回归训练:只有上文,生成模型

LLM 编码器 怎么实现语义相关的 Token 向量更贴近? 目录 LLM 编码器 怎么实现语义相关的 Token 向量更贴近?mask训练:上下文存在自回归训练:只有上文,生成模型一、核心机制:损失函数与反向传播的“语义校准”1. 损失函数的“语义约束”2. 嵌入层参数的“动态调整”二、关…

从OCR瓶颈到结构化理解来有效提升RAG的效果

当人们探讨如何让人工智能系统更好地从文档中查找和使用信息时&#xff0c;通常关注的是令人瞩目的算法和前沿的大型语言模型。但问题是&#xff1a;如果文本提取的质量很差&#xff0c;那么后续的努力都将付诸东流。本文探讨OCR质量如何影响检索增强生成&#xff08;RAG&#…

SpringBoot -- 整合Junit

11.SpringBoot 整合 Junit 11.1 为什么需要单元测试 由于在SpringBoot开发过程中&#xff0c;每开发一个模块&#xff0c;有时需要从 controller、service、mapper 到甚至 xml 文件的编写全部开发完毕才能进行测试&#xff0c;这是十分浪费时间的&#xff0c;比如开发人员想测…

虚拟机远程连接编译部署QT程序

概要 逻辑 我们需要凑齐 QT库、交叉编译工具、sysroot这三大件。 交叉编译的程序是部署到板卡环境运行,需要构建和板卡一样的库环境。 sysroot是我们在虚拟机上自己命名的一个文件夹,包含开发板的运行系统所需的所有文件。 虚拟机是x64版本,开发板是arm64版本。 如果开发板…

基于SpringBoot的智慧旅游系统

以智慧旅游系统的设计与实现为研究对象&#xff0c;旨在通过科技手段提升旅游业的管理效能和游客体验。在系统设计方面&#xff0c;深入分析了地理特征、丰富的文化底蕴以及多样的自然景观。结合这些独特之处&#xff0c;构建了一个多层次的旅游管理系统&#xff0c;包括景点信…

下载最新版本的OpenOCD

Download OpenOCD for Windowsd&#xff1a; https://gnutoolchains.com/arm-eabi/openocd/

Geollama 辅助笔记:raw_to_prompt_strings_geo.py

1 GeoLifePreprocessingDF 1.1 创造函数 1.2 读取原始数据 读取这个DataFrame 1.3 处理原始DataFrame 1.4 生成对应prompt 1.5 打乱轨迹 1.6 打乱轨迹里面的事件

TDengine 如何打破工业实时数据库势力边界?

打破工业实时数据库势力边界&#xff0c;TDengine 时序数据库与工业 SCADA 深度融合 随着 时序数据库&#xff08;Time Series Database&#xff09;的日益普及&#xff0c;越来越多的工业自动化控制&#xff08;工控&#xff09;人员开始认识到其强大能力。然而&#xff0c;时…

渗透靶场:事件和属性被阻止的反射xss

本关很多标签被拦截了&#xff0c;需要使用 burp 模糊测试哪个标签可以用 <a>和<animate>可以使用&#xff0c;<animate>是<svg>标签中用来给动画设定属性的&#xff0c;看看<svg>可不可用 利用<svg>、<animate>、<a>来构造 这…

STM32中Usart的使用

目录 一、USART简介 1.电平标准 2.通信接口 3.硬件电路 4.串口参数以及时序 5.串口时序 二、USART结构介绍 1.USART功能框图 ​编辑 1.1 功能引脚 1.2 数据寄存器 1.3 控制器 1.4 波特率发生器 1.5简化结构图 2.数据帧 一、USART简介 USART&#xff08;Universa…

鸿蒙HarmonyOS 5小游戏实践:数字记忆挑战(附:源代码)

数字记忆挑战游戏&#xff1a;打造提升大脑记忆力的鸿蒙应用 在当今数字时代&#xff0c;人们的记忆力面临着前所未有的挑战。从日常的待办事项到复杂的工作任务&#xff0c;强大的记忆力都是提高效率和表现的关键。本文将介绍一款基于鸿蒙系统开发的数字记忆挑战游戏&#xf…

记录一个C#/.NET的HTTP工具类

记录一个C#/.NET的HTTP工具类 using Serilog; using System.Net; using System.Text; using System.Text.Json;namespace UProbe.Common.Comm.Http {public class HttpClientHelper{/// <summary>/// 发送HttpGet请求/// </summary>/// <typeparam name"T…

深度学习:PyTorch卷积神经网络之图像入门

本文目录&#xff1a; 一、二值图像二、**灰度图像*三、**索引图像**四、**真彩色RGB图像****星空图** 前言&#xff1a;这篇文章开始讲解CNN&#xff0c;此前讲解的人工神经网络&#xff08;ANN&#xff09;如果有小伙伴还不清楚&#xff0c;一定要多看&#xff0c;多练习&…

PyTorch RNN实战:快速上手教程

PyTorch实现RNN的实例 以下是一个使用PyTorch实现RNN的实例代码,包含数据准备、模型定义、训练和评估步骤。 RNN流程图 RNN流程图,在使用t来表示当前时间点(序列中的第t项),RNN接收所有先前内容得单一个表示h和关于序列最新项的信息,RNN将这些信息合并到迄今为止所有看…

C++项目快速配置SQLite

前言&#xff1a;完全没接触过数据库&#xff0c;但老师课程设计要求数据存储在数据库怎么办&#xff1f;&#xff1f;&#xff1f;主包看了些网络上的资源&#xff0c;觉得讲得都不是很能快速上手&#xff0c;所以决定自己写一篇博客 SQLiteCpp是一个基于 C 封装的 SQLite 操…