【问题标题】:Stop MySQL tolerating multiple NULLs in a UNIQUE constraint停止 MySQL 在 UNIQUE 约束中容忍多个 NULL
【发布时间】:2012-03-06 01:04:36
【问题描述】:

我的 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 文档:dev.mysql.com/doc/refman/5.1/en/create-index.html“对于所有引擎,唯一索引允许可以包含 null 的列的多个 null 值”。
  • NULL 不是一个值。正如@MarcB 所说,您需要为此禁止NULL
  • 看起来这是一个错误:bugs.mysql.com/bug.php?id=8173。 (至少错误报告中的一些人是这样认为的)
  • @JNK:错误报告中的最后一篇文章有​​一点恕我直言,它说这是一个错误。但无论如何。
  • @juergend IMO 它仍然不是一个错误,b/c 这太接近将NULL 计算为一个值,它不是。

标签: mysql sql mysql5


【解决方案1】:

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

这取决于MySQL Engine TypeBDB 不允许多个 NULL 值使用 UNIQUEMyISAMInnoDB 允许多个 NULLs 即使使用 UNIQUE

【讨论】:

    【解决方案2】:

    通常,根据存储引擎的不同,NULL 可能被视为唯一值,也可能不被视为唯一值。您必须使用无法将 NULL 识别为唯一值的存储引擎,例如。 InnoDB 或 MyISAM。

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

    【讨论】:

    • -1 用于建议一个幻数解决方案。真正的答案是了解NULL并适当地使用它。
    • 我不会给 -1 因为 OP 似乎认为 NULL 确实是一些神奇的价值。也不 +1,因为不应该鼓励对概念的误解。
    【解决方案3】:

    更新:您应该改用@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 ;
    

    【讨论】:

    • 非常感谢!这个概念很棒,但执行起来太脆弱而无法使用。但是有一个补救措施——这个额外的列应该是TINYINT(1)(意思是——布尔),为空字段持有0,为非空字段持有1(反之亦然,没关系)。唯一索引应该另外使用这个额外的列而不是。就是这样——你不可能因为魔法值而发生冲突,因为没有。
    • @greenoldman 好主意! FWIW,我在具有非常大且活跃的数据集的生产环境中使用这种技术没有任何问题,但我仍然更喜欢你的想法。
    【解决方案4】:

    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 索引中的列指定前缀值,则列值在前缀中必须是唯一的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-12
      • 1970-01-01
      • 2012-01-13
      • 1970-01-01
      • 2018-11-17
      • 1970-01-01
      相关资源
      最近更新 更多