12

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

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;

请注意,Uncle-Child 关系没有 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 中的一个错误)?

4

4 回答 4

8

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

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

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

  1. Uncle.childid 可以为 null,并且您希望为 childid 设置 ON DELETE SET NULL。
  2. Uncle.childid 不能为 null,并且您希望对 childid 进行 ON DELETE CASCADE。
  3. Childid 不属于 Uncle,并且您希望 ChildsUncle 关系具有对 Child 和 Uncle 的 ON DELETE CASCADE 外键约束。Uncleid 将是该关系的候选键(即它应该是唯一的)。
于 2008-09-13T00:08:22.443 回答
3

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

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

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

于 2008-09-16T22:38:24.373 回答
0

@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 中的删除可能仍然失败,例如假设一个具有 fk_child 键的 Friend 表(类似于 Uncle)没有 ON DELETE CASCADE,现在这仍然会导致整个事务失败,因此这对我来说是正确的行为。换句话说,innodb 从可能导致事务可能失败的表开始,但这是我的理论,实际上它可能是一个完全不同的故事。:)

于 2013-05-07T17:51:47.487 回答
-3

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

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

于 2008-09-13T01:02:21.800 回答