题
这个问题上来时我有不同的记录,计数,为什么我认为是相同的查询一个使用 not in
where
约束和另一个 left join
.表中的 not in
约束有一个空值(数据)其引起的,查询返回计数0记录。我有点明白为什么但我可以使用一些帮助完全抓住这一概念。
国家简单地说,为什么不查询的回归结果,但是B不?
A: select 'true' where 3 in (1, 2, 3, null)
B: select 'true' where 3 not in (1, 2, null)
这是在SQL服务器2005年。我也发现,呼叫 set ansi_nulls off
导致B返回的结果。
解决方案
查询A与:
相同select 'true' where 3 = 1 or 3 = 2 or 3 = 3 or 3 = null
由于3 = 3
是真的,你会得到一个结果。
查询B与:
相同select 'true' where 3 <> 1 and 3 <> 2 and 3 <> null
当ansi_nulls
打开时,3 <> null
是UNKNOWN,因此谓词的计算结果为UNKNOWN,并且您没有得到任何行。
当<=>关闭时,<=>为真,所以谓词的计算结果为true,你得到一行。
其他提示
每当你使用NULL时,你实际上都在处理一个三值逻辑。
您的第一个查询返回结果,因为WHERE子句的计算结果为:
3 = 1 or 3 = 2 or 3 = 3 or 3 = null
which is:
FALSE or FALSE or TRUE or UNKNOWN
which evaluates to
TRUE
第二个:
3 <> 1 and 3 <> 2 and 3 <> null
which evaluates to:
TRUE and TRUE and UNKNOWN
which evaluates to:
UNKNOWN
UNKNOWN与FALSE不同 你可以通过调用来轻松测试它:
select 'true' where 3 <> null
select 'true' where not (3 <> null)
两个查询都不会给你带来任何结果
如果UNKNOWN与FALSE相同,那么假设第一个查询会给你FALSE,那么第二个查询必须评估为TRUE,因为它与NOT(FALSE)相同。
事实并非如此。
在SqlServerCentral上有一个非常好的有关此主题的文章
NULL和三值逻辑的整个问题起初可能有点令人困惑,但为了在TSQL中编写正确的查询,必须理解
我建议的另一篇文章是 SQL聚合函数和NULL
NOT IN
与未知值
进行比较时返回0条记录
由于NULL
是未知的,在可能值列表中包含0
或<=>的<=>查询将始终返回<=>记录,因为无法确定< =>值不是被测试的值。
比较null是未定义的,除非你使用IS NULL。
因此,当将3与NULL(查询A)进行比较时,它将返回undefined。
即。 SELECT'true'其中3 in(1,2,null) 和 SELECT'true'其中3不在(1,2,null)
将产生相同的结果,因为NOT(UNDEFINED)仍未定义,但不是TRUE
撰写本文时,这个问题的标题是
SQL NOT IN约束和NULL值
从问题的文本看来,问题出现在SQL DML SELECT
查询中,而不是SQL DDL CONSTRAINT
。
然而,特别是考虑到标题的措辞,我想指出,这里所作的一些陈述可能具有误导性陈述,这些陈述符合(释义)
当谓词评估为UNKNOWN时,你没有得到任何行。
虽然这是SQL DML的情况,但在考虑约束时效果是不同的。
考虑这个非常简单的表,其中两个约束直接取自问题中的谓词(并在@Brannon的优秀答案中解决):
DECLARE @T TABLE
(
true CHAR(4) DEFAULT 'true' NOT NULL,
CHECK ( 3 IN (1, 2, 3, NULL )),
CHECK ( 3 NOT IN (1, 2, NULL ))
);
INSERT INTO @T VALUES ('true');
SELECT COUNT(*) AS tally FROM @T;
根据@Brannon的回答,第一个约束(使用IN
)计算为TRUE,第二个约束(使用NOT IN
)计算为UNKNOWN。 然而,插入成功!因此,在这种情况下,说“<!>”并不严格正确,你没有得到任何行<!>因为我们确实插入了一行作为结果。
对于SQL-92标准,上述效果确实是正确的。比较和对比SQL-92规范
中的以下部分7.6 where where
结果是T表示这些行的表 搜索条件的结果为真。
4.10完整性约束
当且仅当指定时,才满足表检查约束 对于表的任何行,搜索条件都不为假。
换句话说:
在SQL DML中,当WHERE
评估为UNKNOWN时,会从结果中删除行,因为不满足条件<!>“;是真的<!>”。
在SQL DDL(即约束)中,当计算结果为UNKNOWN时,不会从结果中删除行,因为 满足条件<!>“不是false <!>”;
虽然SQL DML和SQL DDL中的效果分别可能看似矛盾,但实际上有理由通过允许UNKNOWN结果满足约束(更准确地说,允许它们不能满足)来给予UNKNOWN结果“怀疑的好处”。约束):没有这种行为,每个约束都必须显式处理空值,从语言设计的角度来看这是非常不令人满意的(更不用说编码器的正确痛苦了!)
P.S。如果你发现遵循<!>这样的逻辑是有挑战性的,那么; unknown不会满足约束<!>因为我要编写它,然后考虑你可以通过避免SQL DDL中的可为空的列以及产生空值的SQL DML中的任何内容(例如外连接)来免除所有这些!
在A中,测试3对集合中每个成员的相等性,产生(FALSE,FALSE,TRUE,UNKNOWN)。由于其中一个元素为TRUE,因此条件为TRUE。 (也可能在这里发生一些短路,所以它一旦达到第一个TRUE就会停止,并且从不评估3 = NULL。)
在B中,我认为它将条件评估为NOT(3 in(1,2,null))。测试3是否与设定的收益相等(FALSE,FALSE,UNKNOWN),它被聚合到UNKNOWN。 NOT(UNKNOWN)产生UNKNOWN。总的来说,条件的真实性是未知的,最终基本上被视为假。
Null表示和缺少数据,即它是未知的,而不是数据值。编程背景的人很容易混淆这一点,因为在C类语言中使用指针时,null确实没什么。
因此,在第一种情况下,3确实在(1,2,3,null)的集合中,因此返回
在第二种情况下,您可以将其缩小为
选择'true',其中3不在(null)
因此,没有返回任何内容,因为解析器对您要比较它的集合一无所知 - 它不是空集而是未知集。使用(1,2,null)没有用,因为(1,2)集显然是错误的,但是那时你正在和那个未知的那个,这是未知的。
可以得出结论从答案在这里, NOT IN (subquery)
不处理空值正确的,应该避免赞成 NOT EXISTS
.然而,这样一个结论可能尚为时过早。在下列情况下,贷记到克里斯的日期(数据库编程和设计,第2卷第9号,1989年), NOT IN
处理空值的正确和返回正确的结果,而不是 NOT EXISTS
.
考虑一个表 sp
代表供应商(sno
)是谁知的供应部分(pno
)在数量(qty
).该表的目前持有以下价值观:
VALUES ('S1', 'P1', NULL),
('S2', 'P1', 200),
('S3', 'P1', 1000)
请注意,数量即可空的为能够记录事实上的供应商是已知的供应部分,即使它不知道在什么样的数量。
任务是找到供应商的供货的一部分数量'P1'但不是在数量为1000。
以下用途 NOT IN
正确地识别供应商'S2'只:
WITH sp AS
( SELECT *
FROM ( VALUES ( 'S1', 'P1', NULL ),
( 'S2', 'P1', 200 ),
( 'S3', 'P1', 1000 ) )
AS T ( sno, pno, qty )
)
SELECT DISTINCT spx.sno
FROM sp spx
WHERE spx.pno = 'P1'
AND 1000 NOT IN (
SELECT spy.qty
FROM sp spy
WHERE spy.sno = spx.sno
AND spy.pno = 'P1'
);
然而,下面查询使用的相同的一般结构,但与 NOT EXISTS
但是,不正确地包括供应商'S1'的结果(即对于其数量是null):
WITH sp AS
( SELECT *
FROM ( VALUES ( 'S1', 'P1', NULL ),
( 'S2', 'P1', 200 ),
( 'S3', 'P1', 1000 ) )
AS T ( sno, pno, qty )
)
SELECT DISTINCT spx.sno
FROM sp spx
WHERE spx.pno = 'P1'
AND NOT EXISTS (
SELECT *
FROM sp spy
WHERE spy.sno = spx.sno
AND spy.pno = 'P1'
AND spy.qty = 1000
);
所以 NOT EXISTS
不是银弹,它可能已经出现了!
当然,问题的根源是存在的空值,因此"真正"的解决办法是消除这些空。
这是可以实现(除其他可能的设计)使用两种表格:
sp
供应商已知供应零件spq
供应商已知供应零件在已知量
注意到有可能应该是一个外国的关键制约因素在那里 spq
参考文献 sp
.
结果后可以得到使用'减'的关系操作员(是的 EXCEPT
关键词标准SQL)如
WITH sp AS
( SELECT *
FROM ( VALUES ( 'S1', 'P1' ),
( 'S2', 'P1' ),
( 'S3', 'P1' ) )
AS T ( sno, pno )
),
spq AS
( SELECT *
FROM ( VALUES ( 'S2', 'P1', 200 ),
( 'S3', 'P1', 1000 ) )
AS T ( sno, pno, qty )
)
SELECT sno
FROM spq
WHERE pno = 'P1'
EXCEPT
SELECT sno
FROM spq
WHERE pno = 'P1'
AND qty = 1000;
如果要使用NOT IN过滤包含NULL的子查询,只需检查not null
SELECT blah FROM t WHERE blah NOT IN
(SELECT someotherBlah FROM t2 WHERE someotherBlah IS NOT NULL )
这是为男孩:
select party_code
from abc as a
where party_code not in (select party_code
from xyz
where party_code = a.party_code);
无论ansi设置如何,这都有效
这也可能有助于了解join,exists和in之间的逻辑区别 http://weblogs.sqlteam.com/mladenp/archive/ 2007/05/18 / 60210.aspx