繁体   English   中英

SQL Server 2008中的递归

[英]Recursive in SQL Server 2008

我有一个这样的方案(表):

在此处输入图片说明

这是表(文件夹)结构。 我在此表中只记录了user_id = 1 现在,我需要为另一个用户插入相同的文件夹结构。

抱歉,我已经更新了问题...是的,folder_id是标识列(但是可以为特定的userID划分folder_id)。 考虑到我不知道可以存在多少个子文件夹。 Folder_Names对于用户而言是唯一的,并且Folder结构对于所有用户而言都不相同。 假设user3需要与user1相同的文件夹结构,而user4需要与user2相同的文件夹结构。 并且仅提供源用户ID和目标用户ID(假设目标用户ID没有任何文件夹结构)。

我怎样才能做到这一点?

您可以执行以下操作:

SET IDENTITY_INSERT dbo.Folder ON
go

declare @maxFolderID int
select @maxFolderID = max(Folder_ID) from Folder

insert into Folder
select @maxFolderID + FolderID, @maxFolderID + Parent_Folder_ID, Folder_Name, 2
from Folder
where User_ID = 1

SET IDENTITY_INSERT dbo.Folder OFF
go

编辑:

SET IDENTITY_INSERT dbo.Folder ON
GO

;
WITH    m AS ( SELECT   MAX(Folder_ID) AS mid FROM     Folder ),
        r AS ( SELECT   * ,
                        ROW_NUMBER() OVER ( ORDER BY Folder_ID ) + m.mid AS rn
               FROM     Folder
                        CROSS JOIN m
               WHERE    User_ID = 1
             )
    INSERT  INTO Folder
            SELECT  r1.rn ,
                    r2.rn ,
                    r1.Folder_Name ,
                    2
            FROM    r r1
                    LEFT JOIN r r2 ON r2.Folder_ID = r1.Parent_Folder_ID


SET IDENTITY_INSERT dbo.Folder OFF
GO

我可以做到这与基于集合的方式非常接近。 问题是,直到这些行实际在表中,我们才能知道将分配哪些新的标识值。 因此,无法使用正确的父值一次性插入所有行。

我在下面使用MERGE ,以便可以在OUTPUT子句中访问源表和inserted表,而INSERT语句则不允许:

declare @FromUserID int
declare @ToUserID int
declare @ToCopy table (OldParentID int,NewParentID int)
declare @ToCopy2 table (OldParentID int,NewParentID int)

select @FromUserID = 1,@ToUserID = 2

merge into T1 t
using (select Folder_ID,Parent_Folder_ID,Folder_Name
       from T1 where User_ID = @FromUserID and Parent_Folder_ID is null) s
on 1 = 0
when not matched then insert (Parent_Folder_ID,Folder_Name,User_ID)
                      values (NULL,s.Folder_Name,@ToUserID)
output s.Folder_ID,inserted.Folder_ID into @ToCopy (OldParentID,NewParentID);

while exists (select * from @ToCopy)
begin
    merge into T1 t
    using (select Folder_ID,p2.NewParentID,Folder_Name from T1
           inner join @ToCopy p2 on p2.OldParentID = T1.Parent_Folder_ID) s
    on 1 = 0
    when not matched then insert (Parent_Folder_ID,Folder_Name,User_ID) 
                          values (NewParentID,Folder_Name,@ToUserID)
    output s.Folder_ID,inserted.Folder_ID into @ToCopy2 (OldParentID,NewParentID);

    --This would be much simpler if you could assign table variables,
    -- @ToCopy = @ToCopy2
    -- @ToCopy2 = null
    delete from @ToCopy;
    insert into @ToCopy(OldParentID,NewParentID)
        select OldParentID,NewParentID from @ToCopy2;
    delete from @ToCopy2;
end

(我也是基于这样的假设写的:我们永远都不希望表中的行具有错误或缺失的父值)


如果逻辑不清楚-我们首先为没有父级的老用户找到行-我们可以立即为新用户复制这些行。 基于此插入,我们跟踪已针对哪个旧标识值分配了哪些新标识值。

然后,我们继续使用此信息来标识要复制的下一组行(在@ToCopy )-因为刚刚复制了其父级的行是有资格复制的下一组行。 我们一直循环,直到产生一个空集,这意味着所有行都已被复制。

这不能解决父/子周期,但是希望您没有任何一个。

假设Folder.Folder_ID是一个标识列,最好分两步进行,第一步是插入所需的文件夹,第二步是更新父文件夹ID。

DECLARE @ExistingUserID INT = 1,
        @NewUserID INT = 2;

BEGIN TRAN;

-- INSERT REQUIRED FOLDERS
INSERT Folder (Folder_Name, User_ID)
SELECT  Folder_Name, User_ID = @NewUserID
FROM    Folder
WHERE   User_ID = @ExistingUserID;

-- UPDATE PARENT FOLDER
UPDATE  f1
SET     Parent_Folder_ID = p2.Folder_ID
FROM    Folder AS f1
        INNER JOIN Folder AS f2
            ON f2.Folder_Name = f1.Folder_Name
            AND f2.user_id = @ExistingUserID
        INNER JOIN Folder AS p1
            ON p1.Folder_ID = f2.Parent_Folder_ID
        INNER JOIN Folder AS p2
            ON p2.Folder_Name = p1.Folder_Name
            AND p2.user_id = @NewUserID
WHERE   f1.user_id = @NewUserID;

COMMIT TRAN;

解决方案2

DECLARE @Output TABLE (OldFolderID INT, NewFolderID INT, OldParentID INT);

DECLARE @ExistingUserID INT = 1,
        @NewUserID INT = 2;

BEGIN TRAN;

MERGE Folder AS t
USING
(   SELECT  *
    FROM    Folder
    WHERE   user_ID = @ExistingUserID
) AS s
    ON 1 = 0 -- WILL NEVER BE TRUE SO ALWAYS GOES TO MATCHED CLAUSE
WHEN NOT MATCHED THEN 
    INSERT (Folder_Name, User_ID)
    VALUES (s.Folder_Name, @NewUserID)
OUTPUT s.Folder_ID, inserted.Folder_ID, s.Parent_Folder_ID 
    INTO @Output (OldFolderID, NewFolderID, OldParentID);

-- UPDATE PARENT FOLDER
UPDATE  f
SET     Parent_Folder_ID = p.NewFolderID
FROM    Folder AS f
        INNER JOIN @Output AS o
            ON o.NewFolderID = f.Folder_ID
        INNER JOIN @Output AS p
            ON p.OldFolderID = o.OldParentID;

COMMIT TRAN;

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM