9

我的 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` )

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

4

4 回答 4

14

警告:此答案已过时。从 MySQL 5.1 开始,不支持 BDB。

这取决于MySQL Engine Type. BDB不允许NULL使用多个值UNIQUEbutMyISAMInnoDB允许多个NULLs 即使使用UNIQUE.

于 2012-02-14T16:49:04.367 回答
2

一般来说,取决于存储引擎,NULL可能会或可能不会被视为唯一值。您必须使用无法识别NULL为唯一值的存储引擎,例如。InnoDB 或 MyISAM。

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

于 2012-02-14T16:49:54.087 回答
1

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


如果您必须强制执行唯一约束但还需要在列上具有外键,那么我找到了解决此问题的方法,因此要求它可以为空。我的解决方案就是由此派生的,需要一点额外的空间。这是一个带有数字 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 ;
于 2014-11-21T02:54:16.010 回答
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 索引中的列指定前缀值,则列值在前缀中必须是唯一的。

于 2014-10-18T10:33:22.790 回答