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

后端 未结 4 927
[愿得一人]
[愿得一人] 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 07:48

    You can use the output clause to capture the modified/inserted rows to a table variable and use that with a delete statement after the merge.

    DECLARE @T TABLE(EmployeeID INT);
    
    MERGE Target1 AS T
    USING Source1 AS S
    ON (T.EmployeeID = S.EmployeeID) 
    WHEN NOT MATCHED BY TARGET AND S.EmployeeName LIKE 'S%' 
        THEN INSERT(EmployeeID, EmployeeName) VALUES(S.EmployeeID, S.EmployeeName)
    WHEN MATCHED 
        THEN UPDATE SET T.EmployeeName = S.EmployeeName
    WHEN NOT MATCHED BY SOURCE AND T.EmployeeName LIKE 'S%'
        THEN DELETE  
    OUTPUT S.EmployeeID INTO @T;
    
    DELETE Source1
    WHERE EmployeeID in (SELECT EmployeeID
                         FROM @T);
    
    0 讨论(0)
  • 2020-12-14 07:52


    Nice Reponse, but your code will delete the row from your destination table, here's an exemple in wich you can delete the rows from your source destination without affecting your target table :

    if OBJECT_ID('audit.tmp1') IS NOT NULL
        DROP TABLE audit.tmp1   
    
    select *
    into audit.tmp1
    from 
    (
    select 1 id, 'aa' nom, convert(date,'2014-01-01') as dd UNION ALL 
    select 2 id, 'bb' nom, convert(date,'2013-07-12') as dd UNION ALL 
    select 3 id, 'cc' nom, convert(date,'2012-08-21') as dd UNION ALL 
    select 4 id, 'dd' nom, convert(date,'2011-11-15') as dd UNION ALL 
    select 5 id, 'ee' nom, convert(date,'2010-05-16') as dd ) T
    
    
    if OBJECT_ID('audit.tmp2') IS NOT NULL
    DROP TABLE audit.tmp2   
    
    select *
    into audit.tmp2
    from 
    (
    select 1 id, 'aAa' nom, convert(date,'2014-01-14') as dd UNION ALL 
    select 2 id, 'bbB' nom, convert(date,'2013-06-13') as dd UNION ALL 
    select 4 id, 'dDD' nom, convert(date,'2012-11-05') as dd UNION ALL 
    select 6 id, 'FFf' nom, convert(date,'2014-01-12') as dd) T
    
    
    SELECT * FROM audit.tmp1 order by 1
    SELECT * FROM audit.tmp2 order by 1
    
    
    DECLARE @T TABLE(ID INT);
    
    MERGE audit.tmp2 WITH (HOLDLOCK) AS T
    USING (SELECT * FROM audit.tmp1 WHERE nom <> 'dd') AS S
    ON (T.id = S.id)
    WHEN NOT MATCHED BY TARGET
    THEN INSERT(id, nom, dd) VALUES(S.id, S.nom, S.dd)
    WHEN MATCHED 
    THEN UPDATE SET T.nom = S.nom, T.dd = S.dd
    WHEN NOT MATCHED BY SOURCE
    THEN UPDATE SET T.id = T.id OUTPUT S.id INTO @T;
    
    DELETE tmp1
    FROM audit.tmp1
    INNER JOIN
    @T AS DEL
        ON DEL.id = tmp1 .id
    
    
    SELECT * FROM audit.tmp1 ORDER BY 1
    SELECT * FROM audit.tmp2 ORDER BY 1
    

    I hope this will help you.

    0 讨论(0)
  • 2020-12-14 07:57

    you can also use below code

    drop table energydata
    
    create table temp_energydata  
    (
    webmeterID int,
    DT DateTime ,
    kWh varchar(10)
    )
    
    Insert into temp_energydata
    select 1,getdate()-10, 120
    union
    select 2,getdate()-9, 140
    union
    select 3,getdate()-6, 37
    union
    select 4,getdate()-3, 40
    union
    select 5,getdate()-1, 240
    
    create table  energydata
    (
    webmeterID int,
    DT DateTime ,
    kWh varchar(10)
    ) 
    
    Insert into energydata (webmeterID,kWh)
    select 1, 120
    union
    select 2, 140
    union
    select 3, 37
    union
    select 4, 40
    
    select * from energydata 
    select * from temp_energydata
    
    begin tran ABC
    
    DECLARE @T TABLE(ID INT);
    
    MERGE INTO dbo.energydata WITH (HOLDLOCK) AS target
    USING dbo.temp_energydata AS source
        ON target.webmeterID = source.webmeterID
        AND target.kWh  = source.kWh 
    
    WHEN MATCHED THEN 
        UPDATE SET target.DT = source.DT
    
    WHEN NOT MATCHED BY source THEN  delete   
     OUTPUT source.webmeterID INTO @T;
    
    
    DELETE temp_energydata
    WHERE webmeterID in (SELECT webmeterID
                         FROM @T);
        --INSERT (webmeterID, DT, kWh)
        --VALUES (source.webmeterID, source.DT, source.kWh)
    
    
    rollback  tran ABC
    commit  tran ABC
    
    0 讨论(0)
  • 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 <insert very bad words here>
            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.

    0 讨论(0)
提交回复
热议问题