停止MySQL在唯一约束中容忍多个null
我的SQL模式是停止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` ) 为什么这是可以容忍的,我如何阻止它?警告
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,我在生产中使用这种技术时没有遇到任何问题,但我还是更喜欢你的想法。