簡體   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