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

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 ;IE。删除子级并不会删除其叔叔,反之亦然。

当我有一个父母和一个有同一个孩子的叔叔时,我删除了父母,它 似乎 像 InnoDB 应该能够“弄清楚”并让级联波及整个家族(即删除 Parent 也会删除 Uncle 和 Child)。然而,相反,我得到以下信息:

  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 中的一个错误)?

有帮助吗?

解决方案

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

系统并没有真正按照您在这里暗示的方式“弄清楚”东西,它只是遵循其约束规则。问题是您创建的模式遇到了不允许它进一步通过的约束。

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

其他提示

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

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

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

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

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

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

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

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top