Alo*_*tan 16 mysql foreign-key database-design delete constraint
我有一个表格,其中存储了用户在我网站上发布的所有论坛消息。消息层次结构是使用嵌套集模型实现的。
以下是该表的简化结构:
现在,该表看起来像这样:
+ ------- + ------------- + -------------- + ---------- + ----------- + ----------- +
| 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 |
+ ------- + ------------- + -------------- + ---------- + ----------- + ----------- +
Run Code Online (Sandbox Code Playgroud)
注意第一行是根消息,这个帖子的树可以显示为:
-- SELECT * FROM forumTbl WHERE Owner_Id = 1 ORDER BY nleft;
MESSAGE (Id = 1)
MESSAGE (Id = 2)
Message (Id = 3)
Message (Id = 4)
Run Code Online (Sandbox Code Playgroud)
当我尝试Owner_Id在单个查询中删除同一行下的所有行时,就会出现我的问题。例子:
DELETE FROM forumTbl WHERE Owner_Id = 1 ORDER BY nright;
Run Code Online (Sandbox Code Playgroud)
上述查询失败并出现以下错误:
错误代码:1451。无法删除或更新父行:外键约束失败(
forumTbl,CONSTRAINTOwner_Id_frgnFOREIGN KEY (Owner_Id) REFERENCESforumTbl(Id) ON DELETE NO ACTION ON UPDATE NO ACTION)
原因是第一行,即根节点(Id=1),在其Owner_Id字段(Owner_Id=1)中也有相同的值,并且由于外键约束导致查询失败。
我的问题是:如何防止这种外键约束循环并删除引用自身的行?有没有办法在不必先将根行的更新为 的情况下做到这一点?Owner_IdNULL
我创建了这个场景的演示:http : //sqlfiddle.com/#!9/fd1b1
谢谢你。
ype*_*eᵀᴹ 11
除了禁用危险且可能导致不一致的外键外,还有两个其他选项需要考虑:
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 ;
Run Code Online (Sandbox Code Playgroud)
如果这样做,那么从树中删除一个节点和所有后代就更简单了。您删除一个节点并通过级联操作删除所有后代:
DELETE FROM forum
WHERE id = 1 ; -- deletes id=1 and all descendants
Run Code Online (Sandbox Code Playgroud)你介入的问题实际上是2个问题。第一个是从具有自引用外键的表中删除对于 MySQL 来说不是一个严重的问题,只要没有引用自身的行即可。如果有一行,如在您的示例中,选项是有限的。禁用外键或使用CASCADE操作。但是如果没有这样的行,删除就成了一个小问题。
因此,如果我们决定在 中存储NULL而不是相同id的内容owner_id,那么您可以在不禁用外键和级联的情况下进行删除。
然后你会偶然发现第二个问题!运行您的查询会引发类似的错误:
DELETE FROM forum
WHERE owner_id = 1 OR id = 1 ;
Run Code Online (Sandbox Code Playgroud)
错误、警告:
无法删除或更新父行:外键约束失败 (rextester.forum, CONSTRAINT owner_id_frgn FOREIGN KEY (owner_Id) REFERENCES forum (id))
但是,此错误的原因将与以前不同。这是因为 MySQL 在删除每一行之后检查每个约束,而不是(应该)在语句的末尾。因此,当在删除其子项之前删除父项时,我们会收到外键约束错误。
幸运的是,对此有一个简单的解决方案,感谢嵌套集模型和 MySQL 允许我们设置删除顺序。我们只需要按 bynleft DESC或 by排序nright DESC,这样可以确保在父级之前删除所有子级:
DELETE FROM forum
WHERE owner_id = 1 OR id = 1
ORDER BY nleft DESC ;
Run Code Online (Sandbox Code Playgroud)
小注意,我们可以(或应该)使用考虑嵌套模型的条件。这是等效的(并且可能使用索引(nleft, nright)来查找要删除的节点:
DELETE FROM forum
WHERE nleft >= 1 AND nright <= 8
ORDER BY nleft DESC ;
Run Code Online (Sandbox Code Playgroud)SET FOREIGN_KEY_CHECKS=0;
DELETE FROM forum WHERE Owner_Id = 1 ORDER BY nright;
SET FOREIGN_KEY_CHECKS=1;
Run Code Online (Sandbox Code Playgroud)
只是不要忘记在这种情况下您必须手动解析 parent_id 显示为 1 时的情况,因为您没有使用级联
| 归档时间: |
|
| 查看次数: |
92450 次 |
| 最近记录: |