Подтвердить что ты не робот

Как удалить из исходного кода команду MERGE в SQL Server 2008?

В настоящее время я использую следующий код слияния, чтобы перенести дату из источника в цель. У меня есть новое требование, чтобы расширить приведенный ниже код, чтобы удалить запись из исходного кода, как только обновление/вставка будет выполняться на цели. Возможно ли это использование слияния (все примеры, которые я вижу в сети, выполняли 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  ;
4b9b3361

Ответ 1

Вы можете использовать предложение вывода для захвата измененных/вставленных строк в переменную таблицы и использовать это с инструкцией 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


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

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

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

Ответ 3

В нашем случае мы хотели использовать 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 "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

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

Ответ 4

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

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