在没有游标的情况下删除SQL表中的树节点

时间:2014-11-15 14:09:47

标签: sql-server tsql

我有一个存储在sql表中的字符串树,这是表的定义。

CREATE TABLE [FileTree] (
    [ID]       INT           NOT NULL,
    [Name]     VARCHAR (MAX) NOT NULL,
    [ParentID] INT           NULL,
    [UserID]   VARCHAR (MAX) NOT NULL
);

我有一个用于删除节点及其子节点的递归删除程序,它使用了一个游标,并且在我之前关于此事的问题上的SO用户指出语法问题后,它运行得非常好。 (Deleting Tree-Nodes in a SQL table

CREATE PROCEDURE DeleteFile 
    @FileID INTEGER,
    @UserID VARCHAR(MAX) 
AS
BEGIN
    DELETE FROM [FileTree] WHERE [ID] = @FileID AND [UserID]=@UserID;
    IF EXISTS(SELECT * FROM [FileTree] WHERE [ParentID] = @FileID AND [UserID]=@UserID)
    BEGIN
       DECLARE FileCursor CURSOR LOCAL FOR 
            SELECT [ID],[UserID] FROM [FileTree] WHERE [ParentID] = @FileID AND [UserID]=@UserID;
       OPEN FileCursor 
       FETCH NEXT FROM FileCursor INTO @FileID , @UserID 
       WHILE @@FETCH_STATUS =0
       BEGIN
            EXEC DeleteFile @FileID,@UserID;
            FETCH NEXT FROM FileCursor INTO @FileID , @UserID ;
       END
    END
  END

然而,对该问题的另一个回复建议使用公用表表达式,我用Google搜索,我认为我真的不明白CTE如何在此过程中替换光标。有什么建议 ?

1 个答案:

答案 0 :(得分:4)

您可以使用递归CTE来获取应删除的ID的所有子项,然后在DELETE语句中使用它:

with all_ids as (
  select id, ParentID
  from FileTree
  where id = 4  -- this is the root ID that should be deleted

  union all

  select c.id, c.ParentID
  from FileTree c
    join all_ids p on p.id = c.ParentID
)
delete from file_tree
where id in (select id from all_ids);

SQLFiddle示例:http://sqlfiddle.com/#!3/ef474f/1