SQL Server 2000 странная проблема подзапроса - PullRequest
2 голосов
/ 01 апреля 2011

Есть странное поведение, которое сводит меня с ума.

У меня есть таблица пользователей и таблица разрешений в старой базе данных SQL Server 2000.Эта база данных беспорядочная, во многих таблицах нет PK и нет связей между таблицами ... но я не мог это исправить (и, честно говоря, я не думаю, что это связано с моей проблемой).

Users:
IDRecord -> PK money
-- other fields

Permissions:
IDRecord -> money (is not a PK)
IDUser   -> money (refers to Users.IDRecord WITHOUT FK)
Function -> varchar
-- other fields

Я хочу получить идентификаторы пользователей без какого-либо разрешения.

Мой первый подход был написать что-то вроде:

select distinct IDRecord 
from Users
where IDRecord not in (
    select IDUser from Permissions
)

, которое не возвращает мне строк.

Но я ЗНАЮ, что есть пользователи без разрешений, поэтому я пишу второй запрос:

select distinct U.IDRecord 
from Users U
left join Permissions P
    on P.IDUser = U.IDRecord
where P.IDRecord is null

, который корректно возвращает пользователей без разрешений.

Итак, в чем проблема??

Почему первый не работает?

1 Ответ

4 голосов
/ 01 апреля 2011

Это ожидаемое поведение.
Ожидается, что SQL имеет трехзначную логику .
Другими словами: для тех пользователей, у которых нет разрешений, ваш подзапрос не возвращает результат (NULL).
Ваше условие WHERE не выполняется в этих случаях, потому что значение никогда не может быть равно или не равно NULL.

Альтернативы:
1) используйте LEFT JOIN (как вы сделали) или
2) используйте NOT EXISTS, например ::

SELECT DISTINCT IDRecord 
  FROM Users u
 WHERE NOT EXISTS (
         SELECT 1 
           FROM Permissions p 
          WHERE p.IDUser = u.IDRecord
       );

Редактировать: Подробнее о том, как 3VL может укусить вас, если вы не будете осторожны:
Возможно, нелогичный результат произойдет, если вы сделаете что-то вроде этого ...

...
WHERE a_column <> 'some value';

Внезапно строки, в которых a_column равен NULL, исчезают из ваших результатов.
Чтобы вернуть их, вы можете сделать это:

...
WHERE (a_column <> 'some value' OR a_column IS NULL);
...