Sql-Server

如何使用 SQL Server 2008 R2 中的 Merge 更新和插入特定行集到現有表?

  • January 22, 2015

我在數據庫中有一個表,我需要根據傳遞給它的記錄來更新它。我得到一個表值參數,它與現有表具有相同的模式(用於所有密集目的)。要求是:

  • 如果 TVP(源)在現有表(目標)中包含具有現有 ID 的記錄,則更新該記錄,如果它在現有表中未標記為已刪除並且其修改日期較新。

    • 狀況:source.Id = target.Id AND target.IsDeleted = 0 AND source.Modified > target.Modified
  • 如果 TVP 包含一條 ID 不存在於現有表中的記錄,則將該記錄插入到該表中。

到目前為止,我已經進行了以下設置。

**已編輯:**此設置可提供所需的最終結果。但是,我不認為這是獲得預期結果的最佳方式。有兩個 select 語句計算 Merge 語句的源表。我想避免以這種方式過濾掉源表。

我的問題是:有沒有辦法實現與下面的查詢相同的結果,但不必使用 Merge 語句中目前使用的兩個 select 語句?

IF OBJECT_ID('tempdb..#target') IS NOT NULL DROP TABLE #target    
IF OBJECT_ID('tempdb..#source') IS NOT NULL DROP TABLE #source

CREATE TABLE #target  ( Id int, Modified int, IsDeleted bit, NoteText varchar(500) )    
INSERT #target VALUES (1, 1, 0, '') 
INSERT #target VALUES (2, 1, 1, '') 
INSERT #target VALUES (3, 3, 0, '') 

CREATE TABLE #source ( Id int, Modified int, NoteText varchar(500) )     
INSERT #source VALUES (1, 2, 'Modified') 
INSERT #source VALUES (2, 2, 'Modified')
INSERT #source VALUES (3, 2, 'Modified')
INSERT #source VALUES (4, 2, 'Modified') 

-- ** NEED HELP WITH OPTIMIZATION HERE **
merge #target as t 
using (select * from #source s where s.id not in (select id from #target where IsDeleted = 1 OR Modified > s.Modified)) as source
on t.id=source.id 
when matched then update set Modified=source.Modified, NoteText = source.NoteText 
when not matched then insert values (id, Modified, 0 ,NoteText) ;

SELECT * FROM #target; 

-- Expected records in #target after operations
-- 1,2,0,'Modified'  ** UPDATED **         
-- 2,1,1,''          ** NOT UPDATED BECAUSE ISDELETED = 1 **
-- 3,3,0,''          ** NOT UPDATED BECAUSE TARGET.MODIFIED > SOURCE.MODIFIED **
-- 4,2,0,'Modified'  ** INSERTED **

drop table #target;
drop table #source

更簡單的寫法MERGE如下:

MERGE #target /* WITH (SERIALIZABLE) */ AS T
USING #source AS S
   ON S.Id = T.Id
WHEN MATCHED
   AND T.IsDeleted = 0
   AND S.Modified > T.Modified
   THEN UPDATE SET
       Modified = S.Modified,
       NoteText = S.NoteText
WHEN NOT MATCHED THEN
   INSERT (Id, Modified, IsDeleted, NoteText)
   VALUES (S.Id, S.Modified, 0, S.NoteText);

SERIALIZABLE提示對於避免潛在的競爭條件是必要的。這種考慮顯然不適用於本地臨時表目標,但更不用說我的疏忽了。

ON從廣義上講,在子句中(或等效地,在 a 中)執行任何類型的過濾幾乎總是錯誤的CTE。該ON子句應僅用於指定源和目標之間的行如何相關。

有關更多詳細資訊,請參閱使用合併插入、更新和刪除數據

源表和目標表也應該有一個唯一的索引或鍵。此外,為了獲得最佳性能,SQL Server 應該能夠保證該MERGE語句最多會影響每個目標行一次。否則,執行計劃將不得不檢測同一行上的多個 DML 操作,以便在發生執行時錯誤時引發錯誤。這通常需要一個排序,並且總是需要一個段、序列項目和斷言。這些都不會使您的命令執行得更快:)

引用自:https://dba.stackexchange.com/questions/89771