停止MySQL在唯一约束中容忍多个null

停止MySQL在唯一约束中容忍多个null,mysql,sql,mysql5,Mysql,Sql,Mysql5,我的SQL模式是 CREATE TABLE Foo ( `bar` INT NULL , `name` VARCHAR (59) NOT NULL , UNIQUE ( `name`, `bar` ) ) ENGINE = INNODB; MySQL允许重复以下语句,从而导致重复 INSERT INTO Foo (`bar`, `name`) VALUES (NULL, 'abc'); 尽管有 UNIQUE ( `name`, `bar` ) 为什么这是可以容忍的,我如何阻止它?警告

我的SQL模式是

CREATE TABLE Foo (
 `bar` INT NULL ,
 `name` VARCHAR (59) NOT NULL ,
 UNIQUE ( `name`, `bar` )
) ENGINE = INNODB;
MySQL允许重复以下语句,从而导致重复

INSERT INTO Foo (`bar`, `name`) VALUES (NULL, 'abc');
尽管有

UNIQUE ( `name`, `bar` )

为什么这是可以容忍的,我如何阻止它?

警告:这个答案已经过时了。从MySQL 5.1开始,不支持BDB。


它取决于MySQL引擎类型
BDB
不允许使用
UNIQUE
的多个
NULL
值,但是
MyISAM
InnoDB
允许多个
NULL
值,通常情况下,根据存储引擎的不同,
NULL
可能被视为唯一值,也可能不被视为唯一值。您必须使用不将
NULL
识别为唯一值的存储引擎,例如InnoDB或MyISAM


为了解决这个问题,您可以创建一个“空值”,例如9999999,您可以将其识别为
null
,因为无法更改存储引擎决定如何处理唯一键中的空值。

BDB不允许使用unique来处理多个空值。 但是MySQL放弃了BDB引擎()

所以现在:

对于所有引擎,唯一索引允许包含NULL的列具有多个NULL值。 如果在唯一索引中为列指定前缀值,则列值在前缀中必须是唯一的


更新:你应该在下面的评论中使用@greenoldman建议的想法。创建一个带有触发器的布尔字段,以根据可为NULL的字段是否为NULL设置值,然后将唯一约束中的布尔字段与定义唯一性的其他字段组合


我找到了一种解决这个问题的方法,如果您必须强制执行unique约束,但也需要在列上有一个外键,从而要求它可以为null。我的解决方案是从导出的,需要一点额外的空间。这是一个带有数字id字段的示例

基本概念是,您必须创建另一个不可为null的字段,该字段将具有可为null的字段的值,并使用触发器将外键复制到该字段中。然后,将对不可为空的重复字段强制执行唯一约束。为此,您需要定义一个默认值为
0
的不可为空字段,如下所示:

ALTER TABLE `my_table` ADD  `uniq_foo` int(10) UNSIGNED NOT NULL DEFAULT '0';
然后您只需定义一些触发器,如下所示:

DROP TRIGGER IF EXISTS `my_table_before_insert`;
DELIMITER ;;
CREATE TRIGGER `my_table_before_insert` BEFORE INSERT ON `my_table`
FOR EACH ROW
BEGIN
    SET NEW.uniq_foo = IFNULL(NEW.foo_id, 0);
END;;
DELIMITER ;

DROP TRIGGER IF EXISTS `my_table_before_update`;
DELIMITER ;;
CREATE TRIGGER `my_table_before_update` BEFORE UPDATE ON `my_table`
FOR EACH ROW
BEGIN
    SET NEW.uniq_foo = IFNULL(NEW.foo_id, 0);
END;;
DELIMITER ;

使其成为主键,这两个字段中都不允许为空。或者使字段不为空。根据mysql文档:“对于所有引擎,唯一索引允许包含null的列有多个null值”。
null
不是一个值。正如@MarcB所说,您需要禁止
NULL
,因为这看起来像是一个bug:。(至少有一些人在bug报告中这样认为)@JNK:bug报告的最后一篇帖子有一个观点,说这是一个bug。但不管怎样。@juergend IMO它仍然不是一个bug,b/c这太接近于将
NULL
作为一个值来计算,而事实并非如此。-1表示一个幻数解决方案。真正的答案是理解
NULL
并适当地使用它。我不会给出-1,因为OP似乎认为
NULL
确实是一些神奇的值。Nor+1,因为不应该鼓励对概念理解不透彻。非常感谢!这个概念很好,但执行太脆弱,无法使用。然而,有一个补救办法——这个额外的列应该是
TINYINT(1)
(意思是--bool)为null保留
0
,为notnull保留
1
(反之亦然,这无关紧要)为null字段。唯一索引应该额外使用此列,而不是相反。就这样——你不可能因为魔法价值观而产生冲突,因为根本就没有。@greenoldman好主意!FWIW,我在生产中使用这种技术时没有遇到任何问题,但我还是更喜欢你的想法。