是IN而不是互斥?

时间:2013-09-18 06:22:00

标签: sql sql-server

我有两张桌子

表1

Column1
_______
   1
   2
   3
   4
   5
   6

表2

Column 1
________
    4
 NULL    //This NULL value added after answering the question, to show the real problem
    5
    6
    7
    8
    9

这是一个示例案例。我试过的时候,

SELECT column1 FROM Table1 WHERE column1 IN (SELECT column1 FROM Table2)

我得到4,5,6

什么时候

SELECT column1 FROM Table1 WHERE column1 NOT IN (SELECT column1 FROM Table2)

我没有得到1,2,3但是没有

在实际情况中,table1的column1是nvarchar(max),table2的column1是varchar(50)。但是,我尝试将两者都转换为varchar(50)。

3 个答案:

答案 0 :(得分:8)

查看IN的文档,具体为:

  

子查询表达式返回的任何空值,使用IN或NOT IN与 test_expression 进行比较,返回UNKNOWN。将空值与IN或NOT IN一起使用会产生意外结果。

您尚未显示,但我某些您的数据中至少隐藏了一个NULL值。

您可以排除NULL(s),然后NOT IN将按您的预期运作:

SELECT column1 FROM Table1
WHERE column1 NOT IN (SELECT t2.column1 FROM Table2 t2
                      WHERE t2.column1 IS NOT NULL)

在手势中,

INNOT IN是对立的,但你必须记住SQL的three-valued logic。想象一下,我们使用表达式形式

编写了IN
a IN (1,2,NULL)

其处理方式与:

相同
a = 1 OR a = 2 or a = NULL

对于a = 1的任何行,我们有:

TRUE OR TRUE OR UNKNOWN

TRUE。对于a = 3的任何行,比方说,我们有:

FALSE OR FALSE OR UNKNOWN

UNKNOWN

现在,以同样的方式考虑NOT IN

a NOT IN (1,2,NULL)

其处理方式与:

相同
a != 1 AND a != 2 AND a != NULL

对于a = 1的任何行,我们有:

FALSE AND TRUE AND UNKNOWN

哪个是FALSE。对于a = 3,我们有:

TRUE AND TRUE AND UNKNOWN

哪个是UNKNOWNNULL的存在意味着无法获得此AND链以产生TRUE值。

答案 1 :(得分:6)

如果您的Table2中有null个值,则可能会发生这种情况。请改用此查询:

select *
from Table1 as t1
where not exists (select * from Table2 as t2 where t2.column1 = t1.column1);

<强> sql fiddle demo

测试查询:

-- Table2 doesn't have null values, works good
SELECT column1 FROM Table1 WHERE column1 IN (SELECT column1 FROM Table2);
SELECT column1 FROM Table1 WHERE column1 NOT IN (SELECT column1 FROM Table2);

insert into Table2
select null;

-- nothing returned by query, because of null values in Table2
SELECT column1 FROM Table1 WHERE column1 NOT IN (SELECT column1 FROM Table2);

-- works good
select *
from Table1 as t1
where not exists (select * from Table2 as t2 where t2.column1 = t1.column1);

这是因为three-valued logic的SQL,请参阅Damien_The_Unbeliever nice explanation。您可以使用not null查询,如下所示:

SELECT column1 FROM Table1 WHERE column1 NOT IN (SELECT column1 FROM Table2 where column1 is not null);

但我更喜欢exists,因为它会隐式过滤掉null(仅仅因为使用=)条件。

作为补充,不要在没有别名的问题中使用类似查询的查询(实际上不是别名,而是点符号,如Table.columnAlias.column),因为您可以拥有{{3} }。始终对列使用点表示法。所以你的查询应该是这样的:

SELECT t1.column1 FROM Table1 as t1 WHERE t1.column1 NOT IN (SELECT t2.column1 FROM Table2 as t2 where t2.column1 is not null);

答案 2 :(得分:3)

这是他们应该避免NOT IN的主要原因,它基于三向逻辑,YES / NO / UNKNOWN: - )

col IN (1,2,NULL) 
is the logically equivalent to 
col=1 OR col=2 OR col=NULL 

col NOT IN (1,2,NULL) 
is the logically equivalent to
col<>1 AND col<>2 AND col<>NULL 

现在任何与NULL的比较都计算为UNKNOWN,只有“col IS(NOT)NULL”是正确的。

如果你有ORed条件,UNKNOWN无关紧要,但在ANDed条件下单个UNKNOWN导致最终UNKNOWN。

当您向外部table1添加NULL并从table2中删除NULL时,您会注意到IN / NOT IN答案集中都缺少此行。

最好的解决方法是使用EXISTS / NOT EXISTS,只有YES / NO,因为评估UNKNOWN的条件被忽略了,简单地视为FALSE。