Как удалить из источника с помощью команды MERGE в SQL Server 2008? - PullRequest
24 голосов
/ 07 сентября 2011

В настоящее время я использую приведенный ниже код слияния для переноса даты из источника в цель.У меня есть новое требование расширить приведенный ниже код, чтобы удалить запись из источника после выполнения обновления / вставки в целевой системе.Возможно ли это с помощью слияния (во всех примерах, которые я вижу в сети, выполнялась операция del / insert / update в целевом объекте, а не в источнике)

    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  ;

Ответы [ 4 ]

33 голосов
/ 07 сентября 2011

Вы можете использовать предложение output, чтобы записать измененные / вставленные строки в табличную переменную и использовать это с оператором delete после слияния.

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);
2 голосов
/ 02 апреля 2014


Хороший ответ, но ваш код удалит строку из таблицы назначения, вот пример, в котором вы можете удалить строки из источника назначения, не затрагивая таблицу назначения:

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

Надеюсь, это поможет вам.

0 голосов
/ 16 июня 2019

В нашем случае мы хотели использовать MERGE для синхронизации нашей внутренней базы данных с внешним источником другой структуры. Автоматические настройки CASCADE не были возможны, потому что мы наслаждаемся множеством циклических отношений, и, действительно, нам не нравится такая дешевая сила в руках недовольных сотрудников. Мы не можем удалить родительские строки до того, как их дочерние строки исчезнут.

Все это делается с помощью молниеносных MERGE, которые используют параметры табличных значений. На сегодняшний день они обеспечивают наилучшую производительность при неприлично низком объеме памяти приложения.

Объединение разрозненных советов для данных MERGE of Orders ...

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

Все делается за один цикл в прямом и обратном направлении и оптимизирован по ключевым показателям. Хотя внутренние значения первичного ключа неизвестны из источника, операция MERGE делает их доступными для операций DELETE.

Customer MERGE использует другую структуру @LiveRows TABLE, следовательно, другой оператор OUTPUT и другие операторы DELETE ...

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

Установка и обслуживание - это немного больно, но так стоит эффективности.

0 голосов
/ 30 декабря 2016

Вы также можете использовать ниже код

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...