現在、以下のマージコードを使用して、ソースからターゲットに日付を移行しています。ターゲットで更新/挿入が実行されると、ソースからレコードを削除するために以下のコードを拡張する新しい要件があります。これはマージを使用して可能ですか(ネット上で見るすべての例では、ソースではなくターゲットで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 ;
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);
素敵な応答ですが、コードは宛先テーブルから行を削除します。ターゲットテーブルに影響を与えずにソース宛先から行を削除できる例を次に示します。
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
これがお役に立てば幸いです。
この場合、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
すべてが1つの甘いループディーループストリーミングラウンドトリップで行われ、キーインデックスで高度に最適化されています。ソースから内部主キー値が不明であっても、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
セットアップとメンテナンスは少し苦労しますが、効率性が得られるほど価値があります。
以下のコードも使用できます
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