1

我正在阅读一篇文章,解释了 join 和 in 和存在子句之间的区别,但我对使用 NOT IN 与 NOT EXISTS 子句时不同结果的解释感到困惑。有人可以澄清为什么 NOT EXISTS 子句与 NOT IN 子句的输出之间存在差异吗?我尝试从表 t2 中删除 NULL 行 (t2.id = 8) 后仍然得到相同的结果。

这是文章中的 SQL 脚本:

CREATE TABLE t1 (id INT, title VARCHAR(20), someIntCol INT)
GO
CREATE TABLE t2 (id INT, t1Id INT, someData VARCHAR(20))
GO

INSERT INTO t1
SELECT 1, 'title 1', 5 UNION ALL
SELECT 2, 'title 2', 5 UNION ALL
SELECT 3, 'title 3', 5 UNION ALL
SELECT 4, 'title 4', 5 UNION ALL
SELECT null, 'title 5', 5 UNION ALL
SELECT null, 'title 6', 5

INSERT INTO t2
SELECT 1, 1, 'data 1' UNION ALL
SELECT 2, 1, 'data 2' UNION ALL
SELECT 3, 2, 'data 3' UNION ALL
SELECT 4, 3, 'data 4' UNION ALL
SELECT 5, 3, 'data 5' UNION ALL
SELECT 6, 3, 'data 6' UNION ALL
SELECT 7, 4, 'data 7' UNION ALL
SELECT 8, null, 'data 8' UNION ALL
SELECT 9, 6, 'data 9' UNION ALL
SELECT 10, 6, 'data 10' UNION ALL
SELECT 11, 8, 'data 11'

这是 SQL 查询及其解释:

-- IN 没有得到正确的结果。-- 这是因为 IN 如何处理 NULL 和三值逻辑 -- NULL 被视为未知数,因此如果 t2.t1id 中有 null -- NOT IN 将返回 NOT TRUE 或 NOT UNKNOWN。两者都不可能是真的。-- 当 t2 表的 t1id 列中有 NULL 时,NOT IN 查询将始终返回一个空集。

SELECT    t1.* 
FROM    t1 
WHERE    t1.id NOT IN (SELECT t1id FROM t2)

-- NOT EXISTS 得到正确的结果

SELECT    t1.* 
FROM    t1 
WHERE    NOT EXISTS (SELECT * FROM t2 WHERE t1.id = t2.t1id)
GO

DROP TABLE t2
DROP TABLE t1

这是文章的链接:http ://weblogs.sqlteam.com/mladenp/archive/2007/05/18/60210.aspx

谢谢!

4

1 回答 1

0

正如我所看到的,您可以在很多情况下将它们用作相同的东西,但您不能忘记它们背后的细节。

可能您可以同时应用NOT IN和获得相同的结果NOT EXISTS,但您可以看到涉及该NULL值的查询差异。因为NOT EXISTS是获取具有NULL值的那些行的唯一方法。

您可以在此示例中更好地看到它:

update cars set c_owner = NULL where c_id = BMW03444

好吧...让我们试试看我们是否有任何尚未售出的汽车库存。

select count(*) from cars where c_owner not it (select c_name from customers);

输出:

计数(*):0

失败在哪里?非常简单。您不是在请求一组其买家未包含在列表中的汽车。您只是在要求没有所有者的汽车。任何人,即使他不在名单上。正确的形式是:

select count(*) 
from cars c1 
where not exists (
  select c_owner 
  from customers c2
  where c1.c_owner=c2.customer_id
);

计数(*):1

这是因为NOT IN需要特定的值才能签入。所以NULL值被设置为FALSE而不被计算在内。 NOT EXISTS检查集合中不存在元素,因此将NULL值设置为 TRUE 并包含在内。

于 2013-08-04T23:16:41.573 回答