MySQL-删除具有外键约束且引用自身的行


12

我有一个表,用于存储用户在我的网站上发布的所有论坛消息。消息层次结构是使用嵌套集模型实现的

以下是该表的简化结构:

  • ID(主键)
  • Owner_Id(对ID的外国主要参考)
  • Parent_Id(对ID的外国主要参考)
  • 好吧
  • 级别

现在,表格看起来像这样:

+ ------- + ------------- + -------------- + ---------- + ----------- + ----------- +
| Id      | Owner_Id      | Parent_Id      | nleft      | nright      | nlevel      |
+ ------- + ------------- + -------------- + ---------- + ----------- + ----------- +
| 1       | 1             | NULL           | 1          | 8           | 1           |
| 2       | 1             | 1              | 2          | 5           | 2           |
| 3       | 1             | 2              | 3          | 4           | 3           |
| 4       | 1             | 1              | 6          | 7           | 2           |
+ ------- + ------------- + -------------- + ---------- + ----------- + ----------- +

请注意,第一行是根消息,该帖子的树可以显示为:

-- SELECT * FROM forumTbl WHERE Owner_Id = 1 ORDER BY nleft;

MESSAGE (Id = 1)
    MESSAGE (Id = 2)
        Message (Id = 3)
    Message (Id = 4)

当我尝试Owner_Id在单个查询中删除同一行下的所有行时,会发生我的问题。例:

DELETE FROM forumTbl WHERE Owner_Id = 1 ORDER BY nright;

上面的查询失败,并出现以下错误:

错误代码:1451。无法删除或更新父行:外键约束失败(forumTbl,CONSTRAINT Owner_Id_frgnFOREIGN KEY(Owner_Id)参考forumTblId)上的DELETE NO ACTION ON UPDATE NO ACTION)

其原因是,在第一行,也就是根节点Id=1),也具有同样的价值在其Owner_Id领域(Owner_Id=1),它导致查询失败是由于外键约束。

我的问题是:如何防止这种外键约束的圆度并删除引用其自身的行?有没有一种方法无需先将Owner_Id根行的更新为NULL

我创建了这种情况的演示:http : //sqlfiddle.com/#!9/fd1b1

谢谢。

Answers:


9
  1. 除了禁用危险的外键并可能导致不一致之外,还有两个其他选项可供考虑:

  2. FOREIGN KEY使用ON DELETE CASCADE选项修改约束。我尚未测试所有情况,但是您肯定需要(owner_id)外键,也可能需要其他情况。

    ALTER TABLE forum
        DROP FOREIGN KEY owner_id_frgn,
        DROP FOREIGN KEY parent_id_frgn ;
    ALTER TABLE forum
        ADD CONSTRAINT owner_id_frgn
            FOREIGN KEY (owner_id) 
            REFERENCES forum (id)
            ON DELETE CASCADE,
        ADD CONSTRAINT parent_id_frgn
            FOREIGN KEY (parent_id) 
            REFERENCES forum (id)
            ON DELETE CASCADE ;

    如果执行此操作,则从树中删除节点和所有后代会更简单。您删除节点,并通过级联操作删除所有后代:

    DELETE FROM forum
    WHERE id = 1 ;         -- deletes id=1 and all descendants
  3. 您介入的问题实际上是2个问题。首先是,只要没有引用自身的行,从具有自引用外键的表中删除对于MySQL来说并不是一个严重的问题。如您的示例中所示,如果有一行,则选项受到限制。禁用外键或使用CASCADE操作。但是,如果没有这样的行,删除将成为一个较小的问题。

    因此,如果我们决定在中存储NULL而不是将其存储id在中owner_id,则可以在不禁用外键且没有级联的情况下进行删除。

    然后,您会发现第二个问题!运行查询会引发类似的错误:

    DELETE FROM forum 
    WHERE owner_id = 1 OR id = 1 ; 

    错误,警告:
    无法删除或更新父行:外键约束失败(rextester.forum,CONSTRAINT owner_id_frgn FOREIGN KEY(owner_Id)参考论坛(id))

    该错误的原因将与以前不同。这是因为MySQL会在删除每一行后检查每个约束,而不是在语句结束时(应如此)进行检查。因此,如果在删除父项之前先删除其父项,则会收到外键约束错误。

    幸运的是,对此有一个简单的解决方案,即嵌套集模型的thnx,MySQL允许我们为删除设置顺序。我们只需要按nleft DESC或按排序nright DESC,以确保在父级之前删除所有子级:

    DELETE FROM forum 
    WHERE owner_id = 1 OR id = 1 
    ORDER BY nleft DESC ; 

    小提示,我们可以(或应该)使用考虑嵌套模型的条件。这是等效的(并且可以使用索引(nleft, nright)来查找要删除的节点:

    DELETE FROM forum 
    WHERE nleft >= 1 AND nright <= 8 
    ORDER BY nleft DESC ; 

5
SET FOREIGN_KEY_CHECKS=0;
DELETE FROM forum WHERE Owner_Id = 1 ORDER BY nright;
SET FOREIGN_KEY_CHECKS=1;

只是不要忘记在这种情况下,您必须手动分析当parent_id显示为1时的情况,因为您不使用层叠

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.