Почему в моем предложении SQL «NOT IN» результаты «NOT EXISTS» отличаются - PullRequest
3 голосов
/ 04 июня 2010

У меня есть два 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

Может ли кто-нибудь пролить свет на то, почему два начальных запроса не дают одинаковую цифру.

Ответы [ 3 ]

6 голосов
/ 04 июня 2010

У вас есть NULL в подзапросе SELECT l.event_id FROM locations AS l, поэтому NOT IN всегда оценивается как неизвестное и возвращает 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)

Причину такого поведения можно увидеть из приведенного ниже примера.

'x' NOT IN (NULL, 'a', 'b')

≡ 'x' <> NULL и 'x' <> 'a' и 'x' <> 'b'

≡ Неизвестно, Истина и Истина

≡ Неизвестно

5 голосов
/ 04 июня 2010

Форма NOT IN работает по-разному для NULL. Наличие одного 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 быстрее.]

0 голосов
/ 04 июня 2010

In и Exists обрабатываются совсем по-разному.

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, тогда как первый запрос может использовать индекс на Т1 (х).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...