为什么 'WHERE NOT' 的 NULL 行不起作用?

Why isn't 'WHERE NOT' with NULL-rows working?

也许我只是漏掉了什么。由于问题太不具体,无法 google 它/搜索它,我现在在这里寻求帮助。在 MySQL

中测试

我的问题:为什么这个SELECT不输出[id=2]?

SELECT `id` FROM `testtable1` WHERE NOT (`key_boolean` = 1);

Table 定义和测试数据:

CREATE TABLE IF NOT EXISTS `testtable1` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`key_boolean` tinyint NULL DEFAULT NULL,
PRIMARY KEY (`id`)
) AUTO_INCREMENT=1;

INSERT INTO `testtable1` (`key_boolean`) VALUES (1);
INSERT INTO `testtable1` () VALUES ();

中table现存:

id key_boolean
1 1
2 NULL

编辑: 根据答案,我实际上在 Whosebug 上发现了一些类似的问题。这个 post 很适合 MySQL: MYSQL syntax not evaluating not equal to in presence of NULL

NULL 在概念上意味着不确定。所以 NOT NULL 也是 NULL,而且是假的。如果要包含 NULL,可以测试 CASE WHEN key_boolean=1 THEN 0 ELSE 1 END

key_booleanNULL 时,表达式 key_boolean = 1 的计算结果为 NULL。所以你中间有 NOT NULL for NOT (key_boolean = 1)NOT NULL 反过来也计算为 NULL。由于 NULL 不是真的(但也不是假的——在 SQL 中应用 ternary logic),该行不是 selected.

除少数情况外,所有比较运算,如 =,如果其中一个操作数是 NULL,则计算结果为 NULL。 MySQL 中的一个没有 table 异常,在这种情况下,是 NULL-safe equal <=>,它的行为就像您想要的那样。

您可以 select 测试中的(子)表达式 table 并亲自查看它们的结果。

SELECT *,
       key_boolean = 1,
       NOT (key_boolean = 1),
       key_boolean <=> 1,
       NOT (key_boolean <=> 1)
       FROM testtable1;

db<>fiddle

就我个人而言,我发现@sticky-bit 和@ysth 的回答非常有帮助,但我想分享一些关于我的研究的更多信息:

结论: SQL 遵循三值逻辑 (3VL)。这意味着除了 TRUE(1) 和 FALSE(0) 之外还有 NULL。这意味着如果比较中的操作数之一为 NULL,则结果也为 NULL。另请参阅维基百科上的真相 table:https://en.wikipedia.org/wiki/Null_(SQL)#Comparisons_with_NULL_and_the_three-valued_logic_(3VL)


没有NOT的例子1:

SELECT id FROM testtable1 WHERE (key_boolean = 1);, 所以:

  • [第 1 行] key_boolean = 1 --> 1 = 1 --> 真
  • [第 2 行] key_boolean = 1 --> NULL = 1 --> NULL

-> 所以,第 1 行是 selected.

例2,和例1一样,只是求反,所以​​with NOT:

SELECT id FROM testtable1 WHERE NOT (key_boolean = 1);,所以

  • [第 1 行] NOT (key_boolean) = 1 --> 1 = 1 --> TRUE
  • [第 2 行] NOT (key_boolean) = 1 --> NULL = 1 --> NULL

-> 空结果。


问题解决方案:

正如@sticky-bit 已经写的那样,MySQL中有NULL-safe equal,在这个例子中也达到了预期的效果。 <=> 不幸的是,它只适用于 MySQL(然后也适用于 MariaDB)。我在 https://modern-sql.com/feature/is-distinct-from 找到了不同方言的比较。 @ysth 关于“CASE WHEN”的建议可能适用于所有方言,但会爆炸 select-terms.

在我的具体情况下(不再是实际问题的一部分)我现在用 COALESCE() 解决了它 - 确定了 NULL 值的默认值,因为在我的情况下,我不仅必须支持 mysql,参见:

SELECT `id` FROM `testtable1` WHERE NOT (COALESCE(`key_boolean`, 0) = 1);

进一步阅读: 如果像我一样从未听说过 NULL 安全等于运算符 <=>,请参阅:What is this operator <=> in MySQL?

现在我绝不是数据库技术人员,但知道 NULL 是未定义的(来自其他编程语言)。对我来说,一个明显的问题是为什么至少 SQL 不每次都使用 NULL-safe equal。请参阅:Is there a reason not to use <=> (null safe equals operator) in mysql instead of =? and Is there a reason not to use <=> (null safe equals operator) in mysql instead of =? [每个都带有标记 post]。