为什么我的SQL'NOT IN'子句产生与'NOT EXISTS'不同的结果
我有两个SQL查询产生不同的结果,当我期望他们产生相同的结果时。我试图找到没有相应位置的事件数量。所有地点都有一个事件,但事件也可以链接到非地点记录。为什么我的SQL'NOT IN'子句产生与'NOT EXISTS'不同的结果
以下查询产生的计数为16244,正确的值。
SELECT COUNT(DISTINCT e.event_id)
FROM events AS e
WHERE NOT EXISTS
(SELECT * FROM locations AS l WHERE l.event_id = e.event_id)
以下查询将产生的0
SELECT COUNT(DISTINCT e.event_id)
FROM events AS e
WHERE e.event_id NOT IN (SELECT l.event_id FROM locations AS l)
计数下面的SQL做一些数据的汇总设置
SELECT 'Event Count',
COUNT(DISTINCT event_id)
FROM events
UNION ALL
SELECT 'Locations Count',
COUNT(DISTINCT event_id)
FROM locations
UNION ALL
SELECT 'Event+Location Count',
COUNT(DISTINCT l.event_id)
FROM locations AS l JOIN events AS e ON l.event_Id = e.event_id
,并返回结果如下
Event Count 139599 Locations Count 123355 Event+Location Count 123355
任何人都可以解释为什么2个初始查询不会产生相同的数字。
您在子查询SELECT l.event_id FROM locations AS l
有一个NULL那么NOT IN will always evaluate to unknown and return 0 results
SELECT COUNT(DISTINCT e.event_id)
FROM events AS e
WHERE e.event_id NOT IN (SELECT l.event_id FROM locations AS l)
这样做的原因的行为可以从下面的例子可以看出。
'X' NOT IN(NULL, 'A', 'B')
≡的 'x' <> NULL和 'X' <> 'a' 和 'X' <>' b”
≡未知和真时真
≡未知
对于NULL,NOT IN
表单的工作方式不同。单个NULL
的存在将导致整个语句失败,从而不会返回任何结果。
因此,您至少有一个event_id
在locations
即NULL
。
此外,您的查询可能更好的写法如下联接:
SELECT
COUNT(DISTINCT e.event_id)
FROM
events AS e
LEFT JOIN locations AS l ON e.event_id = l.event_id
WHERE
l.event_id IS NULL
[更新:显然,NOT EXISTS
版本速度更快。]
+1谢谢。出于兴趣,为什么它会被更好地写成连接?对于我的数据集来说,联接版本的速度稍慢,但根据执行计划对存在和联接进行了非常类似的处理,但JOIN执行了合并联接(左外部),而EXISTS执行了合并联接(左侧反半) – 2010-06-04 11:51:11
@Steve它不会。参见http://sqlinthewild.co.za/index.php/2010/03/23/left-outer-join-vs-not-exists/ – 2010-06-04 12:02:57
谢谢马丁。这是一篇很棒的文章。 – 2010-06-04 12:21:50
在和存在处理方式非常不同。
Select * from T1 where x in (select y from T2)
通常处理为:
select *
from t1, (select distinct y from t2) t2
where t1.x = t2.y;
子查询被评估,distinct'ed,索引(或散列或排序),然后加入到 原始表 - 通常。
与之相对
select * from t1 where exists (select null from t2 where y = x)
也就是说更像处理:
for x in (select * from t1)
loop
if (exists (select null from t2 where y = x.x)
then
OUTPUT THE RECORD
end if
end loop
它总是导致T1的完全扫描而第一查询可以利用一个索引 对T1( X)。
在我的具体情况下,查询优化器对两个查询都使用完全相同的查询计划。 – 2010-06-04 11:55:17
͏͏͏͏͏+ 1为好文章。你是怎么找到它的? – Pacerier 2015-04-12 17:43:24
@Pacerier这个答案是5岁,所以不能肯定记得。可能我已经意识到这篇文章以前碰到过它。 – 2015-04-12 18:16:42