删除 SQL 表中的分层数据

Posted

技术标签:

【中文标题】删除 SQL 表中的分层数据【英文标题】:Deleting hierarchical data in SQL table 【发布时间】:2009-05-19 12:08:38 【问题描述】:

我有一个包含分层数据的表。 一列“ParentId”,其中包含其父项的 Id(“ID” - 键列)。

删除一行时,我想删除所有子项(所有级别的嵌套)。

怎么做?

谢谢

【问题讨论】:

【参考方案1】:

在 SQL Server 上:使用递归查询。给定 CREATE TABLE tmp(Id int, Parent int),使用

WITH x(Id) AS (
    SELECT @Id
    UNION ALL
    SELECT tmp.Id
      FROM tmp
      JOIN x ON tmp.Parent = x.Id
)
DELETE tmp
  FROM x
  JOIN tmp ON tmp.Id = x.Id

【讨论】:

【参考方案2】:

添加外键约束。以下示例适用于 mysql (syntax reference):

ALTER TABLE yourTable
ADD CONSTRAINT makeUpAConstraintName
FOREIGN KEY (ParentID) REFERENCES yourTable (ID)
ON DELETE CASCADE;

这将在数据库级别进行操作,dbms 将确保一旦删除了一行,所有引用的行也将被删除。

【讨论】:

SQL Server 2005 不支持自引用级联删除。当您尝试删除包含“子”行的行时会出错。 作者在我写这个答案的时候还没有指定一个 DBMS。我将其留作参考。 嗯,这很公平。感谢您的澄清。 ON DELETE CASCADE 也适用于 Oracle。这是我认为最好的答案(如果您的 DBMS 支持的话)。 澄清一下,我只在主从关系为一级深度并在两个表中建模的情况下使用此方法。【参考方案3】:

当行数不太大时,erikkallen 的递归方法有效。

这是使用临时表收集所有子表的替代方法:

create table #nodes (id int primary key)
insert into #nodes (id) values (@delete_id)
while @@rowcount > 0
    insert into #nodes 
    select distinct child.id 
    from table child
    inner join #nodes parent on child.parentid = parent.id
    where child.id not in (select id from #nodes)

delete
from table
where id in (select id from #nodes)

它从带有@delete_id 的行开始,然后从那里下降。 where 语句是为了防止递归;如果你确定没有,你可以忽略它。

【讨论】:

我的sql没那么强。所以我问这个:你为什么需要:"select id from table where id = @delete_id" 为什么不能只使用@delete_id 作为值? 如果这比递归 CTE 表现更好,我会感到非常惊讶【参考方案4】:

取决于您存储层次结构的方式。如果您只有 ParentID,那么它可能不是您采取的最有效的方法。为了便于子树操作,您应该有一个额外的列Parents,它将存储所有父 ID,例如:

/1/20/25/40

这样您就可以通过以下方式获取所有子节点:

where Parents like @NodeParents + '%'

第二种方法 除了 ParentID,您还可以有 leftright 值。这样做的插入速度较慢,但​​选择操作非常快。尤其是在处理子树节点时...http://en.wikipedia.org/wiki/Tree_traversal

第三种方法 如果您使用 SQL 2005+,请检查递归 CTE

第四种方法 如果您使用 SQL 2008,请检查 HierarchyID 类型。它为您的情况提供了足够的可能性。 http://msdn.microsoft.com/en-us/magazine/cc794278.aspx

【讨论】:

不,我不想将整个父母链存储在一个列中,因为涉及到不断变化的父母。并且很难跟踪所有这些。不能像现在这样做吗? 对 Hierarchy 数据的主要操作是什么?是插入、更新还是读取? 我倾向于同意第一种方法 - 我们有分层数据表,我们正在做同样的事情。它有助于摆脱孩子,如果您需要对树进行基于路径的处理(例如必须快速返回父母的所有孩子进行计算),它也会有所帮助。我们最初尝试使用触发器来维护这一点,但确实发现添加大量数据时对性能的影响令人望而却步。 如果您使用的是 SQL Server 2005,那么您肯定应该研究第三种方法。这样,您就不必维护具有多个属性的父母列,这很麻烦。【参考方案5】:

像这样向表中添加触发器

在 myTable 上创建触发器 TD_MyTable 以删除为 -- 删除一级子级 delete M from deleted D inner join myTable M 在 D.ID = M.ID

每次删除都会在同一张表上调用一次删除,重复调用触发器。在线查看书籍以了解其他规则。触发器可以嵌套的次数可能有限制。

ST

【讨论】:

SQL SERVER 2005 Express 中可用的那些触发器? 我相信它们是,但是你必须自己写出来。它没有向导。 我猜触发器会起作用,但触发器的问题是它会在每次删除时激活,即使在我只想删除一行的情况下......【参考方案6】:

取决于您的数据库。如果您使用的是 Oracle,您可以这样做:

DELETE FROM Table WHERE ID IN (
  SELECT ID FROM Table
  START WITH ID = id_to_delete
  CONNECT BY PRIOR.ID = ParentID
)

预计到达时间:

如果没有 CONNECT BY,它会变得有点棘手。正如其他人所建议的那样,触发器或级联删除约束可能是最简单的。

【讨论】:

我使用的是 MS SQL SERVER 2005 express【参考方案7】:

触发器只能用于深度不超过 32 层的层次结构:

http://sqlblog.com/blogs/alexander_kuznetsov/archive/2009/05/11/defensive-database-programming-fun-with-triggers.aspx

【讨论】:

是的。最大级别深度约为 8,9。无论如何,我不认为我会使用触发器,因为我不需要在数据库上运行的每个删除命令上激活触发器。【参考方案8】:

你想要的是这些表之间的referential integrity。

【讨论】:

以上是关于删除 SQL 表中的分层数据的主要内容,如果未能解决你的问题,请参考以下文章

sql怎么删除一个表中的所有数据

用SQL语句怎么删除表中的所有数据?

用SQL语句怎么删除表中的所有数据?

SQL中清除数据库里面的所有表中数据,要求删除以后表还在。

用sql语句删除表中的某个ID

SQL中表里面怎么删除重复数据