【问题标题】:In what order are ON DELETE CASCADE constraints processed?以什么顺序处理 ON DELETE CASCADE 约束?
【发布时间】:2010-09-08 18:41:05
【问题描述】:

这是我正在做的一个例子:

CREATE TABLE Parent (id BIGINT NOT NULL,
  PRIMARY KEY (id)) ENGINE=InnoDB;

CREATE TABLE Child (id BIGINT NOT NULL,
  parentid BIGINT NOT NULL,
  PRIMARY KEY (id),
  KEY (parentid),
  CONSTRAINT fk_parent FOREIGN KEY (parentid) REFERENCES Parent (id) ON DELETE CASCADE) ENGINE=InnoDB;

CREATE TABLE Uncle (id BIGINT NOT NULL,
  parentid BIGINT NOT NULL,
  childid BIGINT NOT NULL,
  PRIMARY KEY (id),
  KEY (parentid),
  KEY (childid),
  CONSTRAINT fk_parent_u FOREIGN KEY (parentid) REFERENCES Parent (id) ON DELETE CASCADE,
  CONSTRAINT fk_child FOREIGN KEY (childid) REFERENCES Child (id)) ENGINE=InnoDB;

注意叔-孩子关系没有ON DELETE CASCADE;即删除 Child 不会删除其 Uncle,反之亦然。

当我有一个父母和一个叔叔有同一个孩子时,我删除了父母,似乎 InnoDB 应该能够“弄清楚”并让级联通过整个家庭(即删除父母也会删除叔叔和孩子)。但是,相反,我得到以下信息:

  ERROR 1451 (23000): Cannot delete or update a parent row: a foreign key constraint fails (`cascade_test/uncle`, CONSTRAINT `fk_child` FOREIGN KEY (`childid`) REFERENCES `child` (`id`))

InnoDB 正在尝试在引用它的 Uncle 之前级联删除 Child。

我错过了什么吗?这应该是否由于某种我不明白的原因而失败?或者有什么技巧可以让它工作(或者它是 MySQL 中的一个错误)?

【问题讨论】:

    标签: mysql database-design mysql-error-1451


    【解决方案1】:

    在更简单的情况下,如果从 Child 中删除一条记录并且它有一个引用 Uncle 会发生什么?这是未指定的,因此无论如何约束都会失败。

    如果删除 Child 不会删除其 Uncles,那么会发生什么? Uncle.childid 不能为空。

    你想要的是以下三件事之一:

    1. Uncle.childid 可以为 null,并且您希望 childid 的 ON DELETE SET NULL。
    2. Uncle.childid 不能为 null,并且您希望 ON DELETE CASCADE for childid。
    3. Childid 不属于 Uncle,并且您希望 ChildsUncle 关系具有对 Child 和 Uncle 的 ON DELETE CASCADE 外键约束。 Uncleid 将是该关系的候选键(即它应该是唯一的)。

    【讨论】:

      【解决方案2】:

      如您所说,父删除正在触发子删除,我不知道为什么它会在叔表之前进入子表。我想您必须查看 dbms 代码才能确定,但​​我确定有一种算法可以选择首先级联到哪些表。

      系统并没有真正按照您在此处暗示的方式“找出”东西,它只是遵循其约束规则。问题是您创建的架构遇到了一个不允许它进一步通过的约束。

      我明白你在说什么.. 如果它首先击中 uncle 表,它将删除记录然后删除子项(而不是从子项删除中击中叔叔级联)。但即便如此,我认为不会建立一个模式来依赖于现实中的这种行为。我认为确定发生了什么的唯一方法是查看代码或让这里的 mysql/postgresql 程序员之一说明它是如何处理 fk 约束的。

      【讨论】:

        【解决方案3】:

        @Matt Solnit 首先,这确实是一个好问题,据我所知,何时要删除 Parent 中的记录,然后 innodb 首先尝试确定哪些其他表包含对它的引用,以便它可以删除记录也来自他们。在您的情况下,它是 Child 表和 Uncle 表,现在看来,在这种情况下,它决定首先从 Child 表中删除记录,因此它对 Child 重复相同的过程并最终失败,因为 Uncle 持有对 Child 表的引用,但既不是“ON在 Uncle 表中为 fk_child FK 指定了 DELETE CASCADE”或“ON DELETE SET NULL”。但是,似乎 if innodb 首先尝试从 Uncle 表中删除记录,然后删除应该顺利进行。好吧,在重新考虑之后,我认为由于 innodb 遵循 ACID 模型,因此它选择 Child 而不是 Uncle 来启动删除过程,因为如果它以 Uncle 开头,即使那么 Child 中的删除可能仍然失败,例如假设一个 Friend 表具有 fk_child 键(类似于 Uncle)而没有 ON DELETE CASCADE,现在这仍然会导致整个事务失败,因此这对我来说是正确的行为。换句话说,innodb 从可能导致事务可能失败的表开始,但这是我的理论,实际上它可能是一个完全不同的故事。 :)

        【讨论】:

          【解决方案4】:

          设计全错了。你应该有一个表,有父子关系(字面意思)。 然后你可以通过查询找出叔叔(和阿姨)

          select id from persons where -find all children of the grandparents
          parent id in (
          select parentid from persons --find the grandparents
          where id in (
          select parentid from persons --find the parents
          where id=THECHILD) )
          minus --and take out the child's parents
          select parentid from persons
          where id=THECHILD

          【讨论】:

          • 嗨托尼。问题是这只是一个例子。在现实生活中,这些表根本不包含相同的数据。这只是说明关系的一种简单方法。
          猜你喜欢
          • 2013-04-19
          • 2013-01-30
          • 2015-11-07
          • 2018-12-02
          • 1970-01-01
          • 2012-12-06
          • 2015-02-17
          • 2015-12-31
          • 1970-01-01
          相关资源
          最近更新 更多