13

我正在使用以下查询:

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 视为特殊?

4

6 回答 6

32

这 :

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'无法确定,甚至不是真的,甚至不是假的

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

can't be determined AND can't bedetermined

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

select null <> 'Bruce' 

那返回空值。

这也返回 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

什么都不会退回

于 2012-05-30T05:27:56.433 回答
1

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

于 2012-05-30T05:25:15.167 回答
1

INNULL如果左侧的表达式是 ,则返回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);
于 2012-05-30T05:27:32.767 回答
1

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

想象一下,我们有一个表,其中包含从11,000,000in的所有数字,id并且这个查询:

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对于所有其他值,谓词返回1and 2,因此返回and 。NULL12

在它的对面:

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
             )

, 谓词FALSE1and2NULL所有其他值返回,因此不返回任何内容。

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

于 2012-05-30T07:28:24.897 回答
1

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

@Michael 在他的帖子中说:


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

在语义上等价于:

错误<>“超时”和错误<>“连接错误”

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

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

所以,是的,可悲的是,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] 中的规范中。我认为语言和技术应该更简单,因为你应该能够在不需要阅读规范的情况下进行推断。确实,2 与 NULL 不同,我应该负责控制和处理更高抽象级别的错误,但是 MySQL 应该在将 NULL 与特定值进行比较时抛出 FALSE 结果。

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

于 2014-10-29T15:29:11.370 回答
1

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

我同意本论坛 [2] 中的@Wagner Bianchi 的观点:<< 处理数据和子查询时非常有技巧>>

此外,这不应该是这种行为,我认为 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

于 2014-10-29T15:49:59.980 回答