为什么我的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”

≡未知和真时真

≡未知

+0

͏͏͏͏͏+ 1为好文章。你是怎么找到它的? – Pacerier 2015-04-12 17:43:24

+0

@Pacerier这个答案是5岁,所以不能肯定记得。可能我已经意识到这篇文章以前碰到过它。 – 2015-04-12 18:16:42

对于NULL,NOT IN表单的工作方式不同。单个NULL的存在将导致整个语句失败,从而不会返回任何结果。

因此,您至少有一个event_idlocationsNULL

此外,您的查询可能更好的写法如下联接:

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版本速度更快。]

+0

+1谢谢。出于兴趣,为什么它会被更好地写成连接?对于我的数据集来说,联接版本的速度稍慢,但根据执行计划对存在和联接进行了非常类似的处理,但JOIN执行了合并联接(左外部),而EXISTS执行了合并联接(左侧反半) – 2010-06-04 11:51:11

+1

@Steve它不会。参见http://sqlinthewild.co.za/index.php/2010/03/23/left-outer-join-vs-not-exists/ – 2010-06-04 12:02:57

+0

谢谢马丁。这是一篇很棒的文章。 – 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)。

+0

在我的具体情况下,查询优化器对两个查询都使用完全相同的查询计划。 – 2010-06-04 11:55:17