我的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` )
为什么容忍这种情况?如何阻止它?
答案 0 :(得分:12)
警告:此答案已过时。从MySQL 5.1开始,不支持BDB。
取决于MySQL Engine Type
。 BDB
使用NULL
不允许多个UNIQUE
值,MyISAM
和InnoDB
允许使用NULL
多个UNIQUE
。
答案 1 :(得分:2)
通常,根据存储引擎,NULL
可能会或可能不会被视为唯一值。您必须使用不会将NULL
识别为唯一值的存储引擎,例如。 InnoDB或MyISAM。
要解决此问题,您可以创建一个“空值”,例如99999999,您可以将其识别为NULL
,因为无法更改存储引擎决定处理唯一键中的空值的方式。
答案 2 :(得分:1)
更新:您应该在下面的评论中使用@greenoldman建议的想法。创建一个带触发器的布尔字段,根据您的可空字段是否为NULL来设置值,然后将唯一约束中的布尔字段与定义唯一性的其他字段组合。
如果你必须强制执行唯一约束但是还需要在列上有一个外键,那么我找到了解决这个问题的方法,因此要求它可以为空。我的解决方案来自this,需要额外的空间。这是一个带有数字id字段的示例。
基本概念是你必须创建另一个不可为空的字段,该字段具有可空字段的值,并且外键与触发器重复。然后,将对不可空的重复字段强制执行唯一约束。为此,您需要定义一个不可为空的字段,其默认值为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 ;
答案 3 :(得分:0)
BDB不允许使用UNIQUE多个NULL值。 但是MySQL放弃了BDB引擎(http://dev.mysql.com/doc/relnotes/mysql/5.1/en/news-5-1-12.html)。
现在:http://dev.mysql.com/doc/refman/5.5/en/create-index.html
对于所有引擎,UNIQUE索引允许包含NULL的列的多个NULL值。 如果为UNIQUE索引中的列指定前缀值,则列值在前缀中必须是唯一的。