为什么MYSQL IN关键字不考虑NULL值

时间:2012-05-30 05:22:03

标签: mysql mysql-python

我使用以下查询:

select count(*) from Table1 where CurrentDateTime>'2012-05-28 15:34:02.403504' and Error not in ('Timeout','Connection Error');

令人惊讶的是,此语句不包含具有错误值为NULL的行。我的意图是仅过滤具有错误值的行作为'超时' (或)'连接错误'。我需要提供一个附加条件(OR Error为NULL)来检索正确的结果。

为什么MYSQL使用NULL值过滤结果? 我认为IN关键字会返回一个布尔结果(1/0),现在我明白一些MYSQL关键字不会返回布尔值,它也可能返回NULL ....但为什么将NULL视为特殊?

6 个答案:

答案 0 :(得分:23)

这:

Error not in ('Timeout','Connection Error');

在语义上等同于:

Error <> 'TimeOut' AND Error <> 'Connection Error'

关于空比较的规则也适用于IN。因此,如果Error的值为NULL,则数据库无法使表达式成为真。

要修复,你可以这样做:

COALESCE(Error,'') not in ('Timeout','Connection Error');

或者更好:

Error IS NULL OR Error not in ('Timeout','Connection Error');

或者更好的是:

 CASE WHEN Error IS NULL THEN 1
 ELSE Error not in ('Timeout','Connection Error') THEN 1
 END = 1

OR不会短路,CASE会以某种方式使您的查询短路


也许一个具体的例子可以说明NULL NOT IN expression为什么不返回任何内容:

鉴于此数据:http://www.sqlfiddle.com/#!2/0d5da/11

create table tbl
(
  msg varchar(100) null,
  description varchar(100) not null
  );


insert into tbl values
('hi', 'greet'),
(null, 'nothing');

你做这个表达:

select 'hulk' as x, msg, description 
from tbl where msg not in ('bruce','banner');

只会输出'hi'。

NOT IN翻译为:

select 'hulk' as x, msg, description 
from tbl where msg <> 'bruce' and msg <> 'banner';

NULL <> 'bruce'无法确定,甚至不是真的,甚至不是假的

NULL <> 'banner'无法确定,甚至不是真的

所以null值表达式,有效地解析为:

can't be determined AND can't bedetermined

事实上,如果您的RDBMS支持SELECT上的布尔值(例如MySQL,Postgresql),您可以看到原因:http://www.sqlfiddle.com/#!2/d41d8/828

select null <> 'Bruce' 

返回null。

这也返回null:

select null <> 'Bruce' and null <> 'Banner'

鉴于您使用的是NOT IN,它基本上是一个AND表达式。

NULL AND NULL

结果为NULL。所以就像你在做:http://www.sqlfiddle.com/#!2/0d5da/12

select * from tbl where null

不会返回任何内容

答案 1 :(得分:1)

因为null未定义,所以null不等于null。您始终必须显式处理null。

答案 2 :(得分:1)

如果左侧的表达式为IN,则

NULL会返回NULL。要获得NULL值,您必须执行以下操作:

select count(*) from Table1 where CurrentDateTime>'2012-05-28 15:34:02.403504' and (Error not in ('Timeout','Connection Error') or Error is null);

答案 3 :(得分:1)

IN返回一个三价BOOLEAN(接受NULL作为值)。 NOT IN返回IN的三价否定,NULL的否定为NULL

想象一下,我们在1中有一个包含1,000,000id的所有数字的表格以及此查询:

SELECT  *
FROM    mytable
WHERE   id IN (1, 2, NULL)

或等同物:

SELECT  *
FROM    mytable
WHERE   id = ANY
             (
             SELECT  1
             UNION ALL
             SELECT  2
             UNION ALL
             SELECT  NULL
             )

对于TRUE,谓词返回1,对于所有其他值,谓词返回2NULL,因此会返回12。< / p>

在它的对立面:

SELECT  *
FROM    mytable
WHERE   id NOT IN (1, 2, NULL)

,或

SELECT  *
FROM    mytable
WHERE   id <> ALL
             (
             SELECT  1
             UNION ALL
             SELECT  2
             UNION ALL
             SELECT  NULL
             )

,对于所有其他值,谓词返回FALSE 12以及NULL,因此不返回任何内容。

请注意,布尔否定不仅会将运算符(=更改为<>),还会将量词(ANY更改为ALL)。

答案 4 :(得分:0)

@Michael Buen的答案对我的案子来说是正确的答案,但让我简化原因。

@Michael在帖子中说:

  
     

错误不在('超时','连接错误');

     

在语义上等同于:

     

错误&lt;&gt; 'TimeOut'和错误&lt;&gt; '连接错误'

     

关于空比较的规则也适用于IN。因此,如果Error的值为NULL,则数据库无法使表达式为true。

在[1]中,我发现这句话证实了他最重要的陈述,用于理解为什么IN失败为NULL。在[1]中的规范(“specs”)中,您将:“如果一个或两个参数为NULL,则比较结果为NULL,但NULL安全&lt; =&gt;相等比较运算符除外。” p>

所以是的,遗憾的是Mysql在这种情况下迷失了。我认为Mysql设计者不应该这样做,因为当我将2与NULL进行比较时,Mysql应该能够看到它们是不同的,而不是简单地抛出错误的结果。例如,我做了:

select id from TABLE where id not in (COLUMN WITH NULLS);

然后它会抛出EMPTY结果。但。如果我做

select id from TABLE where id not in (COLUMN WITH OUT NULLS);

它显示了正确的结果。因此,在使用IN运算符时,必须过滤掉NULLS。作为用户,这不是我想要的行为,但它在[1]的规范中有记载。我认为语言和技术应该更简单,因为你应该能够在不需要阅读规范的情况下进行DEDUCE。确实,2与NULL不同,我应该是负责控制和处理更高抽象级别错误的人,但MySQL在将NULL与特定值进行比较时应该抛出一个FALSE结果。

规格参考:[1] http://dev.mysql.com/doc/refman/5.6/en/type-conversion.html

答案 5 :(得分:0)

很抱歉在同一个论坛上发帖两次,但我想说明另一个例子:

我同意@Wagner Bianchi在[2]的论坛中说: &LT;&LT;处理数据和子查询&gt;&gt;

时,这是非常巧妙的

此外,这不应该是行为,我认为Mysql的设计师在[1]中记录这个决定时会犯错误。设计应该是不同的。让我解释一下:比较时你知道吗

select (2) not in (1, 4, 3);
    you will get:
        +----------------------+
        | (2) not in (1, 4, 3) |
        +----------------------+
        |                    1 |
        +----------------------+
        1 row in set (0.00 sec)

但是如果在列表中你至少有一个NULL,那么:

select (2) not in (1, NULL, 3);
    throws:
        +-------------------------+
        | (2) not in (1, NULL, 3) |
        +-------------------------+
        |                    NULL |
        +-------------------------+
        1 row in set (0.00 sec)
    This is pretty absurd.

我们不是第一个对此感到困惑的人。见[2]

参考文献:

[1] http://dev.mysql.com/doc/refman/5.0/en/comparison-operators.html#function_in

[2] http://blog.9minutesnooze.com/sql-not-in-subquery-null/comment-page-1/#comment-86954