SQL Server 2000奇怪的子查询问题

问题描述:

有一种奇怪的行为令我疯狂。SQL Server 2000奇怪的子查询问题

我有一个用户表和旧的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 

是正确返回用户没有权限。

那么,问题在哪里呢?

为什么第一个不起作用?

+0

很奇怪的行为 – 2011-04-01 07:23:29

+0

事实证明,如果列表中(无论是手动或子查询)中至少有一个NULL不是永远不返回TRUE。看起来像是按照设计。 – 2011-04-01 08:45:40

+0

因此,作为Adam解决方案的替代方案,您可以修改子查询,如下所示:'从IDUser不为空的权限中选择IDUser。或者,也许是时候查看您的模式,并根本禁止该列中的NULL。 – 2011-04-01 08:47:47

这是预期的行为。
这是因为SQL有three-valued logic
换句话说:对于那些没有权限的用户,子查询不会返回结果(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); 
+0

太棒了!它如你所说的那样工作。但你能更具体吗?我不明白为什么我的“不在”版本失败。那么,哪里应该使用“不在”条款?非常感谢! – tanathos 2011-04-01 07:36:52

+0

我又增加了3VL相关的例子。 WRT'NOT IN',我从来没有使用它:D – bernie 2011-04-01 07:39:19

+0

OMG,我用过'NOT IN'这么多次......也许这个问题只与sql server 2000有关,甚至到2005和2008? – tanathos 2011-04-01 07:47:29