代码之家  ›  专栏  ›  技术社区  ›  Matthew

TSQL Parent>Child>Sub-Child复制(无光标)

  •  4
  • Matthew  · 技术社区  · 14 年前

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

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

    我已经用一个 fast_forward cursor .

    有没有更好的方法来完成这个任务而不使用光标?

    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 回复  |  直到 14 年前
        1
  •  6
  •   Quassnoi    14 年前

    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
    
        2
  •  1
  •   Matthew    14 年前

    好的,这是 MERGE 我提出了一个基于Quassnoi的解决方案。我应该适当地工作,而不是 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
    
        3
  •  1
  •   Josh Crozier HBP    9 年前

    我看到这篇文章时,我几乎被它的复杂性吓得喘不过气来,但它看起来确实不错。当我需要克隆或复制包含子表或子表的表时,我只需向表中添加一个名为 PreCloneControl

    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
    
        4
  •  0
  •   Luka Milani    14 年前

    要提高SP的速度,可以添加另一条语句 FOR READ ONLY

        ...
    
    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
    
    ...