为什么我的SQL'NOT IN'子句会产生与'NOT EXISTS'不同的结果

时间:2010-06-04 11:13:24

标签: sql sql-server-2005

当我希望它们产生相同的结果时,我有两个SQL查询产生不同的结果。我试图找到没有相应位置的事件数。所有地点都有活动,但活动也可以链接到非地点记录。

以下查询产生的计数为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个初始查询不会产生相同的数字。

3 个答案:

答案 0 :(得分:6)

子查询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'

     

≡未知,真实和真实

     

≡未知

答案 1 :(得分:5)

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

因此event_idlocations至少有NULLSELECT 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}}版本更快。]

答案 2 :(得分:-1)

存在的处理方式非常不同。

Select * from T1 where x in ( select y from T2 )

通常按以下方式处理:

select * 
  from t1, ( select distinct y from t2 ) t2
 where t1.x = t2.y;

对子查询进行评估,区分,索引(或散列或排序),然后加入 原始表 - 通常。

相反
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)。