[英]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.