How to delete from source using MERGE command in SQL Server 2008?

后端 未结 4 939
[愿得一人]
[愿得一人] 2020-12-14 07:35

I am currently using the below merge code to migrate date from source to target. I have a new requirement to extend the below code to delete the record from source once an u

4条回答
  •  北荒
    北荒 (楼主)
    2020-12-14 08:02

    In our case, we wanted to use MERGE to synchronize our internal database with an outside source of a different structure. Automated CASCADE settings were not an option because we enjoy many cyclical relationships and, really, we don't like that kind of cheap power in the hands of disgruntled staffers. We can't delete parent rows before their child rows are gone.

    All of this is done with lightning fast MERGEs that use Table Value Parameters. They provide, by far, the best performance with obscenely low app memory overhead.

    Combining scattered advice for the MERGE of Orders data...

    CREATE PROCEDURE MyOrderMerge @SourceValues [MyOrderSqlUserType] READONLY
    AS
    BEGIN
        DECLARE @LiveRows TABLE (MergeAction VARCHAR(20), OrderId INT);
        DECLARE @DeleteCount INT;
    
        SET @DeleteCount = 0;
    
        MERGE INTO [Order] AS [target]
    
            USING   (   SELECT  sv.OrderNumber,
                        c.CustomerId,
                        st.ShipTypeId
                        sv.OrderDate,
                        sv.IsPriority
    
                        FROM @SourceValues sv
                            JOIN [Customer] c ON sv.[CustomerName] = c.[CustomerName]
                            JOIN [ShipType] st ON ...
                ) AS [stream]
                ON [stream].[OrderNumber] = [target].[SourceOrderNumber]
    
            WHEN MATCHED THEN
                UPDATE
                    ...
    
            WHEN NOT MATCHED BY TARGET THEN
                INSERT
                    ---
    
            -- Keep a tally of all active source records
            -- SQL Server's "INSERTED." prefix encompases both INSERTed and UPDATEd rows 
            OUTPUT $action, INSERTED.[OrderId] INTO @LiveRows
    
        ;   -- MERGE has ended
    
        -- Delete child OrderItem rows before parent Order rows
    
        DELETE FROM [OrderItem]
            FROM [OrderItem] oi
                -- Delete the Order Items that no longer exist at the source
                LEFT JOIN @LiveRows lr ON oi.[OrderId] = lr.[OrderId]
            WHERE lr.OrderId IS NULL
        ;
    
        SET @DeleteCount = @DeleteCount + @@ROWCOUNT;
    
        -- Delete parent Order rows that no longer have child Order Item rows
    
        DELETE FROM [Order]
            FROM [Order] o
                -- Delete the Orders that no longer exist at the source
                LEFT JOIN @LiveRows lr ON o.[OrderId] = lr.[OrderId]
            WHERE lr.OrderId IS NULL
        ;
    
        SET @DeleteCount = @DeleteCount + @@ROWCOUNT;
    
        SELECT MergeAction, COUNT(*) AS ActionCount FROM @LiveRows GROUP BY MergeAction
        UNION
        SELECT 'DELETE' AS MergeAction, @DeleteCount AS ActionCount
        ;
    END
    

    Everything is done in one sweet loop-dee-loop streamed round trip and highly optimized on key indexes. Even though internal primary key values are unknown from the source, the MERGE operation makes them available to the DELETE operations.

    The Customer MERGE uses a different @LiveRows TABLE structure, consequentially a different OUTPUT statement and different DELETE statements...

    CREATE PROCEDURE MyCustomerMerge @SourceValues [MyCustomerSqlUserType] READONLY
    AS
    BEGIN
        DECLARE @LiveRows TABLE (MergeAction VARCHAR(20), CustomerId INT);
        DECLARE @DeleteCount INT;
    
        SET @DeleteCount = 0;
    
        MERGE INTO [Customer] AS [target]
    
            ...
    
            OUTPUT $action, INSERTED.[CustomerId] INTO @LiveRows
    
        ;   -- MERGE has ended
    
        -- Delete child OrderItem rows before parent Order rows
    
        DELETE FROM [OrderItem]
            FROM [OrderItem] oi
                JOIN [Order] o ON oi.[OrderId] = o.[OrderId]
                    -- Delete the Order Items that no longer exist at the source
                    LEFT JOIN @LiveRows lr ON o.[CustomerId] = lr.[CustomerId]
            WHERE lr.CustomerId IS NULL
        ;
    
        SET @DeleteCount = @DeleteCount + @@ROWCOUNT;
    
        -- Delete child Order rows before parent Customer rows
    
        DELETE FROM [Order]
            FROM [Order] o
                -- Delete the Orders that no longer exist at the source
                LEFT JOIN @LiveRows lr ON o.[CustomerId] = lr.[CustomerId]
            WHERE lr.CustomerId IS NULL
        ;
    
        SET @DeleteCount = @DeleteCount + @@ROWCOUNT;
    
        -- Delete parent Customer rows that no longer have child Order or grandchild Order Item rows
    
        DELETE FROM [Customer]
            FROM [Customer] c
                -- Delete the Customers that no longer exist at the source
                LEFT JOIN @LiveRows lr ON c.[CustomerId] = lr.[CustomerId]
            WHERE lr.CustomerId IS NULL
        ;
    
        SET @DeleteCount = @DeleteCount + @@ROWCOUNT;
    
        SELECT MergeAction, COUNT(*) AS ActionCount FROM @LiveRows GROUP BY MergeAction
        UNION
        SELECT 'DELETE' AS MergeAction, @DeleteCount AS ActionCount
        ;
    END
    

    Setup and maintenance is a bit of a pain - but so worth the efficiencies reaped.

提交回复
热议问题