Sqlserver
 sql >> Base de Dados >  >> RDS >> Sqlserver

Copiar uma linha em cascata com todas as linhas filhas e suas linhas filhas, etc.


Presumo que Blocks.BlockID , Elevations.ElevationID , Floors.FloorID , Panels.PanelID são chaves primárias e IDENTITY geradas automaticamente .
  • Um Block tem muitas Elevations .
  • Uma Elevation tem muitos Floors .
  • Um Floor tem muitos Panels .

Eu usaria MERGE com OUTPUT cláusula.

MERGE pode INSERT , UPDATE e DELETE rows. Neste caso, precisamos apenas de INSERT .

1=0 é sempre falso, então o NOT MATCHED BY TARGET parte é sempre executada. Em geral, pode haver outras ramificações, veja docs.WHEN MATCHED geralmente é usado para UPDATE;WHEN NOT MATCHED BY SOURCE é normalmente usado para DELETE , mas não precisamos deles aqui.

Esta forma complicada de MERGE é equivalente ao simples INSERT ,mas ao contrário do simples INSERT sua OUTPUT A cláusula permite fazer referência às colunas que precisamos. Permite recuperar colunas das tabelas de origem e destino, salvando assim um mapeamento entre os IDs existentes antigos e os novos IDs gerados por IDENTITY .

Bloquear

Copie um determinado Block e lembre-se do ID do novo Block .Podemos usar INSERT simples e SCOPE_IDENTITY aqui, porque BlockID é chave primária e apenas uma linha pode ser inserida.
DECLARE @blockToCopy int = 1;
DECLARE @VarNewBlockID int;
INSERT INTO Blocks
    (ProjectID
    ,BlockName
    ,BlockDescription)
SELECT
    ProjectID
    ,'NewNameTest'
    ,'NewDescTest'
FROM Blocks
WHERE Blocks.BlockID = @blockToCopy
;
SET @VarNewBlockID = SCOPE_IDENTITY();

Elevações

Copie Elevations do antigo Block e atribua-os ao novo Block .Lembre-se do mapeamento entre os IDs antigos e IDs recém-gerados em @MapElevations .
DECLARE @MapElevations TABLE(OldElevationID int, NewElevationID int);

MERGE INTO Elevations
USING
(
    SELECT
        ElevationID
        ,@VarNewBlockID AS BlockID
        ,ElevationName
        ,ElevationDescription
    FROM Elevations
    WHERE Elevations.BlockID = @blockToCopy
) AS Src
ON 1 = 0
WHEN NOT MATCHED BY TARGET THEN
INSERT
    (BlockID
    ,ElevationName
    ,ElevationDescription)
VALUES
    (Src.BlockID
    ,Src.ElevationName
    ,Src.ElevationDescription)
OUTPUT
    Src.ElevationID AS OldElevationID
    ,inserted.ElevationID AS NewElevationID
INTO @MapElevations(OldElevationID, NewElevationID)
;

Andares

Copie Floors usando mapeamento entre o antigo e o novo ElevationID .Lembre-se do mapeamento entre os IDs antigos e IDs recém-gerados em @MapFloors .
DECLARE @MapFloors TABLE(OldFloorID int, NewFloorID int);

MERGE INTO Floors
USING
(
    SELECT
        Floors.FloorID
        ,M.NewElevationID AS ElevationID
        ,Floors.FloorName
        ,Floors.FloorDescription
    FROM
        Floors
        INNER JOIN Elevations ON Elevations.ElevationID = Floors.ElevationID
        INNER JOIN @MapElevations AS M ON M.OldElevationID = Elevations.ElevationID
    WHERE Elevations.BlockID = @blockToCopy
) AS Src
ON 1 = 0
WHEN NOT MATCHED BY TARGET THEN
INSERT
    (ElevationID
    ,FloorName
    ,FloorDescription)
VALUES
    (Src.ElevationID
    ,Src.FloorName
    ,Src.FloorDescription)
OUTPUT
    Src.FloorID AS OldFloorID
    ,inserted.FloorID AS NewFloorID
INTO @MapFloors(OldFloorID, NewFloorID)
;

Painéis

Copie Panels usando mapeamento entre o antigo e o novo FloorID .Este é o último nível de detalhes, então podemos usar INSERT simples e não se lembre do mapeamento de IDs .
INSERT INTO Panels
    (FloorID
    ,PanelName
    ,PanelDescription)
SELECT
    M.NewFloorID
    ,Panels.PanelName
    ,Panels.PanelDescription
FROM
    Panels
    INNER JOIN Floors ON Floors.FloorID = Panels.FloorID
    INNER JOIN Elevations ON Elevations.ElevationID = Floors.ElevationID
    INNER JOIN @MapFloors AS M ON M.OldFloorID = Floors.FloorID
WHERE Elevations.BlockID = @blockToCopy
;