首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> SQL Server 資料庫 >> 關於SqlServer

SQL Server MERGE 語法全解析:單一陳述式實現 UPSERT (INSERT/UPDATE/DELETE) 與資料同步實戰

     編輯:關於SqlServer

為什麼需要 MERGE?

傳統 ETL/同步邏輯需寫三段代碼:

-- 1. 更新現有
UPDATE Target SET ... FROM Target JOIN Source ON ...;
-- 2. 插入新增
INSERT Target (...) SELECT ... FROM Source WHERE NOT EXISTS (SELECT 1 FROM Target WHERE ...);
-- 3. 刪除目標多餘 (可選)
DELETE Target WHERE NOT EXISTS (SELECT 1 FROM Source WHERE ...);

缺點:多次掃描、事務一致性難控制、代碼冗長、並發下易死鎖。
MERGE (SQL Server 2008+):**單一陳述式、單次掃描、原子性、標準 SQL:2003**。

語法結構

MERGE INTO TargetTable AS T          -- 目標表 (別名 T)
USING SourceTable AS S                -- 來源表/子查詢/CTE (別名 S)
ON T.KeyCol = S.KeyCol                -- 匹配條件 (ON 子句)
WHEN MATCHED THEN                     -- 來源與目標均存在
    UPDATE SET T.Col1 = S.Col1, ...   -- 更新動作
WHEN NOT MATCHED BY TARGET THEN       -- 來源有、目標無
    INSERT (Col1, Col2...) VALUES (S.Col1, S.Col2...)  -- 插入動作
WHEN NOT MATCHED BY SOURCE THEN       -- 目標有、來源無 (全量同步才需)
    DELETE;                           -- 刪除動作
-- 可選:OUTPUT $action, inserted.*, deleted.*; -- 記錄異動明細
;

關鍵規則與限制 (必讀)

  • 分號結尾強制:MERGE ... ; 缺少分號報語法錯誤。
  • 來源唯一性:USING 子查詢結果在 ON 欄位上**必須唯一**,否則「同一目標行匹配多行來源」→ 錯誤 The MERGE statement attempted to UPDATE or DELETE the same row more than once.
  • WHEN MATCHED 子句最多兩個:且必須一個 UPDATE 一個 DELETE,且第一個需帶 AND <condition> (如 WHEN MATCHED AND T.Col <> S.Col THEN UPDATE)。
  • WHEN NOT MATCHED BY TARGET 只能 INSERT;BY SOURCE 只能 UPDATE/DELETE。
  • 觸發器:目標表的 AFTER INSERT/UPDATE/DELETE 觸發器**依序各觸發一次** (非每行)。

實戰範例 1:標準 UPSERT (增量同步)

場景:每日從暫存表 Stg_Customer 同步至正式表 Dim_Customer。

MERGE INTO Dim_Customer AS T
USING Stg_Customer AS S
ON T.CustomerID = S.CustomerID
WHEN MATCHED AND (T.Name <> S.Name OR T.Email <> S.Email OR T.Phone <> S.Phone) THEN
    UPDATE SET T.Name = S.Name, T.Email = S.Email, T.Phone = S.Phone, T.ModifiedDate = GETDATE()
WHEN NOT MATCHED BY TARGET THEN
    INSERT (CustomerID, Name, Email, Phone, CreatedDate)
    VALUES (S.CustomerID, S.Name, S.Email, S.Phone, GETDATE())
-- 可選:輸出異動日誌
OUTPUT $action AS ActionType, 
       COALESCE(inserted.CustomerID, deleted.CustomerID) AS CustomerID,
       inserted.Name AS NewName, deleted.Name AS OldName
INTO dbo.CustomerSyncLog (ActionType, CustomerID, NewName, OldName, SyncTime)
VALUES ($action, COALESCE(inserted.CustomerID, deleted.CustomerID), inserted.Name, deleted.Name, GETDATE());

亮點:WHEN MATCHED AND ... 只有實際變更才 UPDATE,減少寫入/觸發器/日誌開銷;OUTPUT 子句完整記錄異動審計追蹤。

實戰範例 2:全量同步 (含刪除)

場景:參數表/字典表定期全量覆蓋,來源為權威主檔。

MERGE INTO dbo.Config AS T
USING (SELECT KeyName, KeyValue, Description FROM MasterDB.dbo.Config) AS S
ON T.KeyName = S.KeyName
WHEN MATCHED AND T.KeyValue <> S.KeyValue THEN
    UPDATE SET T.KeyValue = S.KeyValue, T.Description = S.Description
WHEN NOT MATCHED BY TARGET THEN
    INSERT (KeyName, KeyValue, Description) VALUES (S.KeyName, S.KeyValue, S.Description)
WHEN NOT MATCHED BY SOURCE THEN
    DELETE;  -- 目標有但來源無 = 已廢棄配置,刪除
OUTPUT $action, inserted.KeyName, deleted.KeyName INTO dbo.ConfigChangeLog;

實戰範例 3:處理「來源重複鍵」- 預先去重

-- 來源可能有重複 CustomerID,取最新 (ModifiedDate 最大) 一行
WITH DedupedSource AS (
    SELECT *, ROW_NUMBER() OVER (PARTITION BY CustomerID ORDER BY ModifiedDate DESC) AS rn
    FROM Stg_Customer
)
MERGE INTO Dim_Customer AS T
USING (SELECT CustomerID, Name, Email, Phone FROM DedupedSource WHERE rn = 1) AS S
ON T.CustomerID = S.CustomerID
WHEN MATCHED THEN UPDATE SET ...
WHEN NOT MATCHED THEN INSERT ...;

效能優化實戰建議

  1. 索引:目標表 ON 欄位 (通常是 PK) 必須有叢集索引;來源表 ON 欄位建立非叢集索引。
  2. 批次大小:超大量同步 (百萬行) 分批處理 (如按 CustomerID 範圍/日期分批 MERGE),避免長鎖定/日誌暴增/TempDB 壓力。
  3. 最小化鎖定:WHEN MATCHED AND T.Col <> S.Col 只更新真正變更的行;考慮 READPAST / LOCK HINTS (謹慎)。
  4. 並行度:大表 MERGE 受益於並行計劃,確保 MAXDOP 設定合理,統計資訊最新。
  5. 避免觸發器連鎖:若目標表有複雜觸發器,考慮拆分為分開的 UPDATE/INSERT/DELETE 陳述式並控制事務邊界。

常見錯誤排查表

錯誤訊息原因解決
The MERGE statement attempted to UPDATE or DELETE the same row more than once.來源 ON 欄位有重複值來源預先去重 (ROW_NUMBER/CTE/DISTINCT)
Incorrect syntax near 'MERGE'.缺少分號結尾 或 版本 < 2008加分號;升級或改用分離陳述式
The target table 'X' of the MERGE statement cannot have any enabled triggers if the MERGE statement contains an OUTPUT clause without INTO clause.有 OUTPUT 無 INTO 且目標表有觸發器加上 OUTPUT ... INTO LogTable 或移除 OUTPUT
死鎖並發 MERGE 同一目標表,鎖升級分批、加索引、調整隔離級別、應用層串行化

MSDN 官方文檔

https://technet.microsoft.com/zh-cn/library/bb510625.aspx

總結

MERGE 是 SQL Server **資料同步、UPSERT、緩慢變維度 (SCD) 處理**的首選武器。核心要訣:**來源去重保證唯一性、MATCHED 條件精準過濾變更行、OUTPUT 子句落地審計日誌、分批處理大數據量**。掌握本文三大實戰範例與避坑指南,即可在生產環境安全部署高效、可靠的 MERGE 同步邏輯。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved