关于mysql:ON DELETE CASCADE约束按什么顺序处理?

关于mysql:ON DELETE CASCADE约束按什么顺序处理?

In what order are ON DELETE CASCADE constraints processed?

这是我正在进行的操作的一个示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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。 即删除孩子不会删除其叔叔,反之亦然。

当我有一个父母和一个带相同孩子的叔叔,并且删除了父母时,似乎InnoDB应该能够"弄清楚它"并让整个家庭级联起来(即删除父母会删除叔叔) 以及儿童)。 但是,我得到以下信息:

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


在较简单的情况下,如果从Child中删除一条记录,并且该记录具有引用的Uncle会发生什么?这是未指定的,因此约束仍然会失败。

如果删除孩子没有删除其叔叔,那会发生什么呢? Uncle.childid不能为null。

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

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

  • 如您所述,父级删除操作会触发子级删除操作,但我不知道为什么它会在叔叔表之前进入子级表。我想您肯定需要查看dbms代码才能知道,但是我肯定有一种算法可以选择首先层叠哪些表。

    该系统并没有真正按照您在此处暗示的方式"弄清楚"这些内容,它只是遵循其约束规则。问题在于您创建的架构遇到了不允许其进一步传递的约束。

    我明白你在说什么..如果它先打到叔叔表上,它将删除记录,然后删除子级(而不是从子级删除中打到叔叔级联)。但是即使这样,我也不认为会建立一个架构来依赖现实中的这种行为。我认为唯一可以确定正在发生什么事情的方法是浏览代码或在这里找一个mysql / postgresql程序员来说明它如何处理fk约束。


    @Matt Solnit首先,这确实是一个好问题,据我所知,当要删除来自Parent的记录时,innodb首先尝试确定哪些其他表包含对该记录的引用,以便它可以从中删除记录好。在您的情况下,它是Child表和Uncle表,现在看来,在这种情况下,它决定首先从Child表中删除记录,因此它对Child重复相同的过程,最终由于Uncle保留对Child表的引用而失败,但是都不起作用。在Uncle表中为fk_child FK指定了DELETE CASCADE"或" ON DELETE SET NULL"。但是,如果innodb首先尝试从Uncle表中删除记录,则删除应该顺利进行。再三考虑之后,我认为由于innodb遵循ACID模型,因此它选择了Child而不是Uncle来启动删除过程,因为即使它以Uncle开头,即使Child中的删除可能仍然失败,例如假设一个具有fk_child键(类似于Uncle)但没有ON DELETE CASCADE的Friend表,现在这仍然会导致整个事务失败,因此对我来说这是正确的行为。换句话说,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


    推荐阅读