【问题标题】:mysql circular dependency in foreign key constraints外键约束中的mysql循环依赖
【发布时间】:2012-09-29 11:03:26
【问题描述】:

给定架构:

我需要让每个user_identities.belongs_to 引用一个users.id

同时,每一个users都有一个primary_identity,如图所示。

但是,当我尝试使用 ON DELETE NO ACTION ON UPDATE NO ACTION 添加此引用时,MySQL 会说

#1452 - 无法添加或更新子行:外键约束失败 (yap.#sql-a3b_1bf, CONSTRAINT #sql-a3b_1bf_ibfk_1 FOREIGN KEY (belongs_to) REFERENCES users (id) ON DELETE更新时不采取行动不采取行动)

我怀疑这是由于循环依赖造成的,但我该如何解决(保持参照完整性)?

【问题讨论】:

  • 是users和user_indentities idid, or idbelongs_to?的外键
  • 查看我的编辑:同时每个users都有一个primary_identity,如图所示。所以FK在两者之间,否则“循环依赖” " 在标题中没有意义。

标签: mysql sql database-design referential-integrity


【解决方案1】:

解决这个问题的唯一方法(至少在 MySQL 的有限功能下)允许两个 FK 列中的 NULL 值。使用主要身份创建新用户将如下所示:

insert into users (id, primary_identity)
values (1, null);

insert into identities (id, name, belongs_to)
values (1, 'foobar', 1);

update users 
  set primary_identity = 1
where id = 1;

commit;

此解决方案的唯一缺点是您不能强制用户具有主要身份(因为该列必须可以为空)。


另一种选择是更改为支持延迟约束的 DBMS,然后您可以只插入两行,并且只会在提交时检查约束。或者使用可以有部分索引的 DBMS,然后您可以使用带有 is_primary 列的解决方案

【讨论】:

  • 我猜 postgresql 会是一个不错的选择,这些东西在他们的文档中都有提到。
【解决方案2】:

我不会以这种方式实现它。

从表users 中删除字段primary_identity,并向表user_profiles 添加一个名为is_primary 的附加字段,并将其用作主要配置文件的指示符

【讨论】:

  • +1 设计方面我同意,但为了争论,如何处理循环引用?
  • 不幸的是,这是一个鸡蛋情况。没有另一个,你怎么能创造 1?
  • 嗯,如果我这样做,我无法确保每个用户都只有一个主要身份。
【解决方案3】:

这将防止 FKs 出现 NULL,但仍不会强制存在主要配置文件 - 必须由应用程序管理。

注意Profile 表上的备用键(唯一索引){UserID, ProfileID}PrimaryProfile 上的匹配 FK。

【讨论】:

  • 将 isPrimary 字段添加到身份表会更容易。
【解决方案4】:

我没用过,你可以试试INSERT IGNORE。我会做这两个,每个表一个,这样一旦它们都完成,参照完整性就会得到维护。如果您在事务中执行它们,如果插入第二个有问题,您可以回滚。

由于您忽略了此功能的约束,因此您应该在程序代码中进行检查,否则您的数据库中的数据可能会忽略您的约束。

感谢@Mihai 指出上述问题。另一种方法是在插入时禁用约束,然后重新启用它们。但是,在可能产生超出可接受的开销的大表上 - 试试看?

【讨论】:

  • INSERT IGNORE 不会忽略约束,它只是不会触发错误。通过不触发错误,它允许您完成数百次插入,即使其中一些由于某种原因失败。这意味着任何 INSERT IGNORE 都会静默失败。
  • @MihaiStancu - 谢谢。我通过一些快速的网络搜索 OP 找到了它,显然我没有很好地阅读文档!
【解决方案5】:

问题似乎是您试图将主要身份信息保留在 user_identities 表中。

相反,我建议您将主要用户信息(姓名/电子邮件)放入用户表中。不要对 user_identities 表使用外键。

只有来自 user_identities 表的外键

所有约束现在都可以正常工作,因为它们只是一种方式。

除非主用户(在用户表中)存在,否则无法输入用户身份。同样,如果存在现有子身份(在 user_identities 中),则主用户不应被删除。

您可能希望将表的名称更改为“primary_users”和“secondary_users”,以明确发生了什么。

这听起来好吗?

【讨论】:

    【解决方案6】:

    这个问题是在 How to drop tables with cyclic foreign keys in MySQL 从删除方面提出的,但我认为其中一个答案也适用于这里:

    SET foreign_key_checks = 0;
    INSERT <user>
    INSERT <user identity>
    SET foreign_key_checks = 1;
    

    创建一个事务并一次性提交。我没试过,但它适用于删除,所以我不知道为什么它不适用于插入。

    【讨论】:

    • 嗯,我想知道你是否可以在不禁用 fk 检查的情况下做到这一点,如果你只是把它做成一个事务?
    • 没有交易没有帮助(即当我询问时它已经在交易中)。
    • 这不是外键的用途。
    猜你喜欢
    • 2012-04-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-01
    相关资源
    最近更新 更多