TSQL Parent>小孩>没有光标的子子复制

时间:2010-08-31 21:59:34

标签: sql-server tsql cursors fast-forward

我正在创建一个SQL 2008 R2存储过程来复制一行及其所有子行。

这是一个包含父,子和子子的3层设置 给定我需要创建副本的父ID。

我使用fast_forward cursor解决了这个问题。

我知道我也可以通过行循环执行它,但我不相信这会比这个游标方法更快。你有什么想法?

是否有更好的方法可以在不使用游标的情况下完成此任务?

编辑:我考虑的另一个选项是创建一个临时表,其中包含TBLACStages记录的旧/新PKID。

TBLACStages可能有1到20个相应的行(TBLACUpgrade每行TBLACStages行可能有3行)

CREATE PROCEDURE [dbo].[spDuplicateACUnit]
@pACUnitID bigint = 0 
AS BEGIN
SET NOCOUNT ON;

DECLARE @NewACUnitID bigint = 0

INSERT INTO TBLACUnits ([col1] ,[col2] ,[...] ,[coln]) SELECT [col1] ,[col2] ,[...] ,[coln] FROM TBLACUnits WHERE ACUnitID = @pACUnitID

SELECT @NewACUnitID = SCOPE_IDENTITY()

DECLARE @ACStageID bigint = 0 
    DECLARE @NewACStageID bigint = 0

DECLARE @ACUnitCursor CURSOR

SET @ACUnitCursor = CURSOR LOCAL FAST_FORWARD FOR SELECT ACStageID FROM TBLACStages WHERE TBLACStages.ACUnitID = @pACUnitID

OPEN @ACUnitCursor

FETCH NEXT FROM @ACUnitCursor INTO @ACStageID

WHILE @@FETCH_STATUS = 0 
BEGIN

INSERT INTO TBLACStages ([ACUnitID] ,[col1] ,[col2] ,[...] ,[coln]) SELECT @NewACUnitID ,[col1] ,[col2] ,[...] ,[coln] FROM TBLACStages WHERE TBLACStages.ACStageID = @ACStageID

SELECT @NewACStageID = SCOPE_IDENTITY()

INSERT INTO TBLACUpgrade ([ACStageID] ,[col1] ,[col2] ,[...] ,[coln]) SELECT @NewACStageID ,[col1] ,[col2] ,[...] ,[coln] FROM TBLACUpgrade WHERE TBLACUpgrade.[ACStageID] = @ACStageID

FETCH NEXT FROM @ACUnitCursor INTO @ACStageID 
END

CLOSE @ACUnitCursor DEALLOCATE @ACUnitCursor

END

GO

4 个答案:

答案 0 :(得分:6)

这应该会给你一个想法:

CREATE TABLE t_parent (id INT NOT NULL PRIMARY KEY IDENTITY, value VARCHAR(100))
CREATE TABLE t_child (id INT NOT NULL PRIMARY KEY IDENTITY, parent INT NOT NULL, value VARCHAR(100))
CREATE TABLE t_grandchild (id INT NOT NULL PRIMARY KEY IDENTITY, child INT NOT NULL, value VARCHAR(100))

INSERT
INTO    t_parent (value)
VALUES  ('Parent 1')

INSERT
INTO    t_parent (value)
VALUES  ('Parent 2')

INSERT
INTO    t_child (parent, value)
VALUES  (1, 'Child 2')

INSERT
INTO    t_child (parent, value)
VALUES  (2, 'Child 2')

INSERT
INTO    t_grandchild (child, value)
VALUES  (1, 'Grandchild 1')

INSERT
INTO    t_grandchild (child, value)
VALUES  (1, 'Grandchild 2')

INSERT
INTO    t_grandchild (child, value)
VALUES  (2, 'Grandchild 3')

DECLARE @parent TABLE (oid INT, nid INT)
DECLARE @child TABLE (oid INT, nid INT)

MERGE
INTO    t_parent
USING   (
        SELECT  id, value
        FROM    t_parent
        ) p
ON      1 = 0
WHEN NOT MATCHED THEN
INSERT  (value)
VALUES  (value)
OUTPUT  p.id, INSERTED.id
INTO    @parent;
SELECT  *
FROM    @parent
MERGE
INTO    t_child
USING   (
        SELECT  c.id, p.nid, c.value
        FROM    @parent p
        JOIN    t_child c
        ON      c.parent = p.oid
        ) c
ON      1 = 0
WHEN NOT MATCHED THEN
INSERT  (parent, value)
VALUES  (nid, value)
OUTPUT  c.id, INSERTED.id
INTO    @child;
SELECT  *
FROM    @child;
INSERT
INTO    t_grandchild (child, value)
SELECT  c.nid, gc.value
FROM    @child c
JOIN    t_grandchild gc
ON      gc.child = c.oid
SELECT  *
FROM    t_grandchild

答案 1 :(得分:1)

好的,这是我基于Quassnoi解决方案提出的MERGE。我应该在没有CURSOR

的情况下正常工作
DECLARE @parent TABLE (oid BIGINT, nid BIGINT)
DECLARE @child TABLE (oid BIGINT, nid BIGINT)

MERGE
INTO    TBLACUnits T
USING   (SELECT [col1], [...], [coln] FROM TBLACUnits WHERE ID = @pID) S

ON      1 = 0
WHEN NOT MATCHED THEN
INSERT  ([ACUnitID]
   ,[col1]
   ,[...]
   ,[coln])
VALUES  (S.[ACUnitID]
   ,S.[col1]
   ,S.[...]
   ,S.[coln]])
OUTPUT  S.ACUnitID, INSERTED.ACUnitID
INTO    @parent;

MERGE
INTO    TBLACStages T
USING   (
  SELECT  tt.[nid] 
                       ,TBLACStages.[col1]
                       ,TBLACStages.[...]
                       ,TBLACStages.[coln]
  FROM TBLACStages
  JOIN @parent tt ON tt.oid = TBLACStages.ACUnitID
  ) S
ON      1 = 0
WHEN NOT MATCHED THEN
INSERT  ([ACUnitID]
   ,[col1]
   ,[...]
   ,[coln])
VALUES  ([nid]
   ,[col1]
   ,[...]
   ,[coln])
OUTPUT  S.[ACStageID], INSERTED.[ACStageID]
INTO    @child;

INSERT INTO TBLACUpgrade 
([ACStageID]
   ,[col1]
   ,[...]
   ,[coln])
SELECT  c.[nid]
   ,TBLACUpgrade.[col1]
   ,TBLACUpgrade.[...]
   ,TBLACUpgrade.[coln]
FROM    @child c
JOIN    TBLACUpgrade
 ON      TBLACUpgrade.ACStageID  = c.oid

答案 2 :(得分:1)

我看到这篇文章,我几乎对复杂性感到厌烦,但确定它看起来不错。当我需要克隆或复制带有子项或孙子项的表时,我只需在名为PreCloneControl的表中添加一个新列,然后在新表的子查询中引用此字段,以便快速轻松地查找旧父数据。简单。但是,如果您无权在表中添加列,则通常可以快速入侵。一个例子是Last Modified User字段,通常是一个100左右的nvarchar字符。通常我们需要更新此字段,因此请将旧控制号放在那里然后离开。只需记住在完成后对“上次修改的用户”字段执行快速更新。这是示例,我使用临时表进行测试,但您应该使用真实表。

Declare @OldControl int = 123456

Declare @TT1 Table
(
TT1Control [int] IDENTITY(1,1) NOT NULL,
SomeData nvarchar(20)
)

insert into @TT1
(
SomeData
)
Select SomeDate from LiveTable where LTControl = @OldControl


Declare @NewControl int = SCOPE_IDENTITY()


Declare @TempTT2 Table
(
TT2Control int IDENTITY(1,1) NOT NULL,
TT2TT1FKControl int,
TT2ChildData nvarchar(20),
TT2ModUser nvarchar(100)
)

insert into @TempTT2
(
TT2TT1FKControl,TT2ChildData,TT2ModUser
)
Select @NewControl, TT2ChildData, Cast(TT2Control as nvarchar(100))
From TT2 where TT2TT1FKControl = @OldControl

Select * from @TempTT2

Declare @TT3 Table
(
TT3Control int IDENTITY(1,1) NOT NULL,
TT3TT2FKControl int,
TT3GrandChildData nvarchar(50),
TT3OldTT2Control int
)

Insert Into @TT3
(
TT3TT2FKControl,TT3GrandChildData,TT3OldTT2Control
)
Select t.TT2Control, BookItemItemNumber,TT2.TT2Control
From TT2 inner join GrandChildTable on TT2Control = GCTFKControl
       ,@TempTT2 as t
Where
TT2TT1FKControl = @OldControl
and  t.TT2ModUser = Cast(TT2Control as nvarchar(100))

Select * From @TT3

Update @TempTT2 set TT2ModUser = 'UserName' Where TT2TT1FKControl = @NewControl

Select * from @TempTT2

答案 3 :(得分:0)

要提高SO的速度,您可以添加另一个语句FOR READ ONLY

所以你的SON会是这样的:

    ...

SET @ACUnitCursor = CURSOR LOCAL FAST_FORWARD FOR 

SELECT ACStageID FROM TBLACStages WHERE TBLACStages.ACUnitID = @pACUnitID

FOR READ ONLY  -- add this to increase the speed

OPEN @ACUnitCursor

FETCH NEXT FROM @ACUnitCursor INTO @ACStageID

...