当涉及的字段之一为 NULL 时,MySQL 错误地允许重复条目

Posted

技术标签:

【中文标题】当涉及的字段之一为 NULL 时,MySQL 错误地允许重复条目【英文标题】:MySQL falsely allowing duplicate entries when one of the fields involved is NULL 【发布时间】:2014-03-08 21:17:01 【问题描述】:

使用 InnoDB/mysqli,我有一个简单的表:mytable。该表有四个字段:id (primary, auto_inc)、field1field2field3。都是BIGINT,除了id,可以是NULL

我添加了一个独特的约束,如下所示:

ALTER TABLE mytable ADD UNIQUE INDEX(field1,field2,field3);

但是,我完全能够添加以下行而不会产生任何错误。我希望这会产生一个“重复”错误,但它不会:

INSERT INTO mytable VALUES (NULL,3,NULL)
INSERT INTO mytable VALUES (NULL,3,NULL)

如果所有字段都具有非 NULL 值,它只会生成“重复”错误 - 例如,

INSERT INTO mytable VALUES (2,3,4)
INSERT INTO mytable VALUES (2,3,4)

即使一个(或多个)字段具有NULL 值,我如何告诉 MySQL 生成“重复”错误?

编辑:这是以前作为“错误”添加到 MySQL 的:http://bugs.mysql.com/bug.php?id=25544

【问题讨论】:

【参考方案1】:

您无法比较 NULL(如果您将任何内容与 NULL 进行比较,即使 NULL=NULL,结果始终是 FALSE)此行为记录在 MySQL ref.

一个 UNIQUE 索引创建一个约束,使得索引中的所有值 必须是不同的。如果您尝试添加带有 与现有行匹配的键值。对于所有引擎,唯一 index 允许包含 NULL 的列有多个 NULL 值。

所以我认为唯一的方法是定义列 NOT NULL 或在触发器中处理此问题。

【讨论】:

【参考方案2】:

问题的根源是 - 与 NULL-s 相比。您应该了解 NULL 的逻辑含义。它是“没有价值的”。不是“零值”“未知值”,而是“无值”。这是一个很大的区别。

这就是为什么将唯一索引的一部分设为 NULL 肯定是个坏主意。你不能比较 NULL,因为你不能比较两个值,这两个值都不存在。因此,只要比较不适用于正常方式,DBMS 就不能保持 NULL-s 的唯一性。是的,MySQL 中存在 <=> 运算符(或其他 DBMS 中的 IS NULL) - 但这是关于如何处理与 NULL 值比较的技术解决方案 - 但不是逻辑。

所以你在XY-problem 的中间。不要将 NULL-s 与唯一键一起使用 - 根据 NULL 的定义和唯一键的意图,它们不能存在。从technical viewpoint(参见关于索引创建的部分)来看,NULL=NULL 将始终导致 false - 因此,如果存在另一个 NULL 值,则允许插入 NULL 值。

【讨论】:

【参考方案3】:

转到:How to Use Unique Indexes in MySQL and Other Databases 并查看“MySQL NULLs”部分。

【讨论】:

如果这是正确的,那么答案是它无法完成 - 我根本无法在唯一索引中使用可以为 NULL 的字段并避免我遇到的问题。将来,您对问题的贡献(即简单地提供链接)最好是作为评论而不是答案。 MySQL 在这种情况下将 NULL 值视为无法与另一个值进行比较的空值,因此它不会抛出任何异常。我会提供评论而不是回答,但我是 14 分。一次的缩写;-D 抱歉,我忘记了您需要一定的声誉等级才能添加 cmets :)

以上是关于当涉及的字段之一为 NULL 时,MySQL 错误地允许重复条目的主要内容,如果未能解决你的问题,请参考以下文章

当字段为 NULL 时,TSQL Casting int

Java返回值为null时如何避免报错?

MYSQL建表1064错误?

当字段值之一错误或留空时,不想提交表单

字段的约束条件

MySQL字段的属性应该尽量设置为NOT NULL