代码之家  ›  专栏  ›  技术社区  ›  Matt Solnit

删除级联约束的处理顺序是什么?

  •  9
  • Matt Solnit  · 技术社区  · 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;
    

    注意,对于叔子关系,没有ON-DELETE级联;i、 e.删除子女并不删除其叔叔,反之亦然。

    当我有一个父母和一个叔叔有同一个孩子,我删除了父母,它 似乎 和InnoDB一样,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`))
    

    我错过什么了吗?这是吗 想象上的 因为我不明白的原因而失败?或者有什么诀窍可以让它工作(或者它是MySQL中的一个bug)?

    4 回复  |  直到 15 年前
        1
  •  8
  •   Apocalisp    17 年前

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

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

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

    1. buff.childid可以为null,并且您希望在DELETE上为childid设置null。
    2. Uncle.childid不能为null,您需要对childid执行删除级联。
    3. Childid不属于父级,并且您希望ChildsUncle关系具有对子级和父级的on-DELETE级联外键约束。Uncleid将是该关系的候选键(即,它应该是唯一的)。
        2
  •  3
  •   Arthur Thomas    17 年前

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

    系统并没有像你在这里暗示的那样真正“理解”东西,它只是遵循它的约束规则。问题是您创建的模式遇到了一个约束,该约束不会让它进一步通过。

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

        3
  •  0
  •   sactiw    12 年前

    @Matt Solnit首先,这是一个很好的问题,据我所知,何时删除父记录,然后innodb首先尝试确定哪些其他表包含对它的引用,以便它也可以从这些表中删除该记录。在您的例子中,它是子表和叔叔表,现在看来,在这种情况下,它决定首先从子表中删除记录,因此它对子表重复相同的过程,并最终失败,因为叔叔持有对子表的引用,但没有为叔叔表中的fk_Child fk指定“ON delete CASCADE”或“ON delete SET NULL”。然而,看起来确实如此 如果

        4
  •  -1
  •   Tony BenBrahim    17 年前

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

    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