简体   繁体   English

SQL Server 2008中的递归

[英]Recursive in SQL Server 2008

I've a scenario(table) like this: 我有一个这样的方案(表):

在此处输入图片说明

This is table(Folder) structure. 这是表(文件夹)结构。 I've only records for user_id = 1 in this table . 我在此表中只记录了user_id = 1 Now I need to insert the same folder structure for another user. 现在,我需要为另一个用户插入相同的文件夹结构。

Sorry, I've updated the question... yes, folder_id is identity column (but folder_id can be meshed up for a specific userID). 抱歉,我已经更新了问题...是的,folder_id是标识列(但是可以为特定的userID划分folder_id)。 Considering I don't know how many child folder can exists. 考虑到我不知道可以存在多少个子文件夹。 Folder_Names are unique for an user and Folder structures are not same for all user. Folder_Names对于用户而言是唯一的,并且Folder结构对于所有用户而言都不相同。 Suppose user3 needs the same folder structure of user1, and user4 needs same folder structure of user2. 假设user3需要与user1相同的文件夹结构,而user4需要与user2相同的文件夹结构。 and I'll be provided only source UserID and destination UserID(assume destination userID doesn't have any folder structure). 并且仅提供源用户ID和目标用户ID(假设目标用户ID没有任何文件夹结构)。

How can i achieve this? 我怎样才能做到这一点?

You can do the following: 您可以执行以下操作:

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

EDIT: 编辑:

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

This is as close to set-based as I can make it. 我可以做到这与基于集合的方式非常接近。 The issue is that we cannot know what new identity values will be assigned until the rows are actually in the table. 问题是,直到这些行实际在表中,我们才能知道将分配哪些新的标识值。 As such, there's no way to insert all rows in one go, with correct parent values. 因此,无法使用正确的父值一次性插入所有行。

I'm using MERGE below so that I can access both the source and inserted tables in the OUTPUT clause, which isn't allowed for INSERT statements: 我在下面使用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

(I've also written this on the assumption that we don't ever want to have rows in the table with wrong or missing parent values) (我也是基于这样的假设写的:我们永远都不希望表中的行具有错误或缺失的父值)


In case the logic isn't clear - we first find rows for the old user which have no parent - these we can clearly copy for the new user immediately. 如果逻辑不清楚-我们首先为没有父级的老用户找到行-我们可以立即为新用户复制这些行。 On the basis of this insert, we track what new identity values have been assigned against which old identity value. 基于此插入,我们跟踪已针对哪个旧标识值分配了哪些新标识值。

We then continue to use this information to identify the next set of rows to copy (in @ToCopy ) - as the rows whose parents were just copied are the next set eligible to copy. 然后,我们继续使用此信息来标识要复制的下一组行(在@ToCopy )-因为刚刚复制了其父级的行是有资格复制的下一组行。 We loop around until we produce an empty set, meaning all rows have been copied. 我们一直循环,直到产生一个空集,这意味着所有行都已被复制。

This doesn't cope with parent/child cycles, but hopefully you do not have any of those. 这不能解决父/子周期,但是希望您没有任何一个。

Assuming Folder.Folder_ID is an identity column, you would be best off doing this in two steps, the first step is to insert the folders you need, the next is to update the parent folder ID. 假设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;

Solution 2 解决方案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