EXISTS 与 IN 的 比较

本文详细探讨了SQL中EXISTS与IN操作符的区别,尤其是在处理NULL值时的行为差异,并通过具体例子展示了NOT EXISTS与NOT IN在执行计划和返回结果方面的不同。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

     曾经有位同学问过我这个问题,EXISTS和IN的区别,我当时给他的答复是:执行计划相同,没有区别.现在回想起来,太片面,觉得有必要整理一下两者的异同.
    在SQL SERVER 2000之前的版本,两者其实是有区别的,优化器会为他们生成不同的执行计划,而且EXISTS的性能更好,因为它具有短路功能.在SQL SERVER 2000及之后的版本,优化器通常会为两逻辑等价的查询生成相同的计划.
    大家意识到EXISTS和IN有区别,争议之处都集中在SQL的三值逻辑(true,false,unknown)上,不同于EXISTS,当输入列表包含NULL时,IN实际上回产生一个UNKNOWN逻辑结果.例如: IN(b,c,NULL)的结果是UNKNOWN.然而,因为在筛选器中UNKNOWN与FALSE的处理方式类似,使用IN和EXISTS查询的结果是一样的,所以优化器会产生相同的执行计划.


接下来来比较一下NOT EXISTS和NOT IN是否也一样呢?先看NOT EXISTS的情况:

  1. SELECT Customer,Customer_Description
  2. FROM dbo.Customers As C
  3. WHERE Country='CHINA'
  4. AND NOT EXISTS
  5. (
  6.  SELECT * FROM Orders As O
  7.  WHERE O.Customer=C.Customer
  8. )


    假设Orders表中包含一个Customer为NULL的订单,但它与我们无关,查询依然会得到所有来自CHINA,暂时没有订单的消费者.
该计划扫描Customers表并筛选来自CHINA的消费者.对于每个匹配的消费者,该计划对Orders.Customer 上的索引执行一次查找.在执行计划中会有一个TOP运算符,因为只需确定是否至少有一个订单与该消费者匹配.这就是EXISTS的短路功能.当Orders.Customer列的密度比较大(即包含大量重复),使用TOP特别高效.每个消费者只发生一次查找(Seek),只在叶级(索引的最底层)扫描一行,以查找一个而不是所有的匹配.


如果用NOT IN来解决相同的问题,SQL你可能会写成:

  1. SELECT Customer,Customer_Description
  2. FROM dbo.Customers As C
  3. WHERE Country='CHINA'
  4. AND Customer NOT IN(SELECT Customer FROM dbo.Orders)

    尝试在Orders表中插入一条Customer为NULL的记录,你会发现,用NOT IN的语句返回空集,因为当Orders.Customer列包含NULL时,IN查询永远不会返回

FALSE!!,而是返回TRUE和UNKNOWN,所以NOT IN只返回NOT TRUE或NOT UNKNOWN,不返回TRUE(这句是关键).
    下面举例来解释,还是刚才的例子,假设Orders列表是(a,b,NULL), a IN (a,b,NULL)返回TRUE,那么a NOT IN (a,b,NULL)就返回 NOT TRUE,即FALSE,所以查询不返回a, 那么, c IN (a,b,NULL)时,逻辑结果是UNKNOWN,那么 c NOT IN (a,b,NULL)则返回 NOT UNKNOWN,还是UNKNOWN.所以c也不返回值,就是说,无论Customers表中的值是什么,只要Orders表中的Customer中有NULL值,该查询实际上不会返回任何结果.所以NOT EXISTS和NOT IN不是逻辑等价的.所以他们的执行计划是不一样的,返回的结果也会不一样.
    在这种情况下,我们只要在NOT IN子查询中加一个筛选器排除NULL值就可以了,如上面SQL中,改为SELECT Customer FROM dbo.Orders WHERE Customer IS NOT NULL.
当然,在实际运用中,大家都不可能允许Orders表中的Customer列是NULL值的.但大家还是要注意,在使用NOT IN的时候,要保证子查询不存在NULL值!!
希望Eric Huang同学满意我的回答.

### SQLEXISTSIN 的区别 #### 工作机制差异 EXISTS 子句用于检测是否存在满足特定条件的记录。它返回布尔值,表示子查询的结果集是否为空。如果至少有一条记录符合条件,则整个表达式的计算结果为真;反之则为假。 对于 IN 运算符而言,其功能在于验证某个字段的值是否存在于由子查询或枚举列表所构成的一组候选值之内[^1]。 ```sql SELECT column_name(s) FROM table1 t1 WHERE EXISTS (SELECT 1 FROM table2 t2 WHERE t2.column = t1.column); ``` 上述例子展示了如何利用 EXISTS 来查找那些在 `table2` 中也有对应项存在的 `table1` 行。 相反地,在下面的例子中可以看到 IN 如何运作: ```sql SELECT column_name(s) FROM table1 WHERE some_column IN (SELECT another_column FROM table2); ``` 这段代码旨在获取所有 `some_column` 的值位于来自 `table2` 的 `another_column` 所组成的集合里的那一部分 `table1` 记录。 #### NULL 处理的不同之处 当涉及到可能含有空值的数据列时,两种方法的行为也有所不同。具体来说,只要存在任何一个匹配项不是 NULL ,那么即使其余均为 NULL ,IN 结果仍将是 TRUE 。然而,一旦遇到第一个不等于给定目标值且非 NULL 的情况,EXISTS 就会立即停止搜索并给出肯定答复。因此,在处理含有可能缺失的信息的情况下,应当谨慎考虑选用哪种方式更为合适[^3]。 #### 性能考量 关于性能方面,并不存在绝对意义上的哪一个总是优于另外一个的说法。实际上,这取决于具体的数据库管理系统(DBMS),以及实际操作环境下的多种因素,比如索引的存在否、数据量大小等等。某些情境下 EXIST 可能表现得更好一点,而在另一些时候 IN 则可能会更快一些。值得注意的是,网络上流传着一种观点认为应该无条件优先采用 EXISTS 而摒弃 IN ,但实际上这种做法并不科学合理[^2]。 为了更精确评估两者的效能差距,建议通过查看执行计划来判断哪一个是最佳选项。不同的 DBMS 提供了各自的工具和技术手段去分析查询语句内部的工作流程,从而帮助开发者做出明智的选择[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值