这是我正在进行的一个例子:
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;即删除子项不会删除其叔叔,反之亦然。
当我有一个Parent和一个Uncle同一个Child时,我删除了Parent,它似乎就像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正试图在引用它的叔叔之前级联删除Child。
我错过了什么吗?这个假定是否由于某种原因我不明白而失败?或者是否有一些技巧使其工作(或者它是MySQL中的错误)?
答案 0 :(得分:8)
在更简单的情况下,如果从Child删除记录并且它有引用Uncle会发生什么?这是未指定的,所以无论如何约束都失败了。
如果删除子项不删除其Uncles,那么会发生什么? Uncle.childid不能为空。
你想要的是这三件事之一:
答案 1 :(得分:3)
父删除正在按照你的说法触发子删除,我不知道为什么它会在uncle表之前进入子表。我想你必须要看看dbms代码才能确定,但我确定有一种算法可以选择哪个表级联到第一个。
系统并没有像你所暗示的那样“弄清楚”东西,它只是遵循它的约束规则。问题是您创建的模式遇到的约束不会让它进一步传递。
我看到你在说什么..如果它首先击中了叔叔表,它将删除该记录,然后删除该子(而不是从子删除中击中叔叔级联)。但即使这样,我也不认为架构会依赖于现实中的那种行为。我认为唯一可以确定发生了什么的方法是查看代码或者在这里找一个mysql / postgresql程序员来说明它如何处理fk约束。
答案 2 :(得分:0)
@Matt Solnit首先这是一个很好的问题,据我所知,当一个来自Parent的记录被删除时,innodb首先尝试识别哪些其他表保存对它的引用,以便它可以删除记录他们也是。在你的情况下它是Child表和Uncle表,现在似乎在这种情况下它决定首先从Child表中删除记录,因此它为Child重复相同的过程并最终失败,因为Uncle持有对Child表的引用但是既没有“ON”在Uncle表中为fk_child FK指定DELETE CASCADE“nor”ON DELETE SET NULL“。但是,似乎如果 innodb首先尝试从Uncle表中删除记录,那么删除应该顺利进行。在考虑了第二个想法之后,我认为因为innodb遵循ACID模型,所以它选择Child over Uncle开始删除过程,因为如果它从Uncle开始,那么Child中的删除可能仍然失败,例如假设一个朋友表有fk_child密钥(类似于Uncle)没有ON DELETE CASCADE,现在这仍然会导致整个事务失败,因此这看起来对我来说是正确的行为。换句话说,innodb以表格开头可能会导致交易失败,但这实际上是我的理论,这可能是一个完全不同的故事。 :)
答案 3 :(得分:-1)
设计完全错了。你应该有一个单独的表,与父子关系(一般)。 然后你可以用查询找出叔叔(和阿姨)
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