Cascade copy a row with all child rows and their child rows, etc

前端 未结 1 1941
失恋的感觉
失恋的感觉 2020-12-20 07:20

I\'m attempting to track items in commercial building construction (specifically concrete cladding).

  • A project can have multiple blocks (a block being a single
相关标签:
1条回答
  • 2020-12-20 07:58

    I assume that Blocks.BlockID, Elevations.ElevationID, Floors.FloorID, Panels.PanelID are primary keys and autogenerated IDENTITY.

    • One Block has many Elevations.
    • One Elevation has many Floors.
    • One Floor has many Panels.

    I'd use MERGE with OUTPUT clause.

    MERGE can INSERT, UPDATE and DELETE rows. In this case we need only INSERT.

    1=0 is always false, so the NOT MATCHED BY TARGET part is always executed. In general, there could be other branches, see docs. WHEN MATCHED is usually used to UPDATE; WHEN NOT MATCHED BY SOURCE is usually used to DELETE, but we don't need them here.

    This convoluted form of MERGE is equivalent to simple INSERT, but unlike simple INSERT its OUTPUT clause allows to refer to the columns that we need. It allows to retrieve columns from both source and destination tables thus saving a mapping between old existing IDs and new IDs generated by IDENTITY.

    Block

    Copy one given Block and remember the ID of the new Block. We can use simple INSERT and SCOPE_IDENTITY here, because BlockID is primary key and only one row can be inserted.

    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();
    

    Elevations

    Copy Elevations from old Block and assign them to the new Block. Remember the mapping between old IDs and freshly generated IDs in @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)
    ;
    

    Floors

    Copy Floors using mapping between old and new ElevationID. Remember the mapping between old IDs and freshly generated IDs in @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)
    ;
    

    Panels

    Copy Panels using mapping between old and new FloorID. This is the last level of details, so we can use simple INSERT and don't remember the mapping of 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
    ;
    
    0 讨论(0)
提交回复
热议问题