Sql-Server
如何使用 SQL Server 2008 R2 中的 Merge 更新和插入特定行集到現有表?
我在數據庫中有一個表,我需要根據傳遞給它的記錄來更新它。我得到一個表值參數,它與現有表具有相同的模式(用於所有密集目的)。要求是:
如果 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 操作,以便在發生執行時錯誤時引發錯誤。這通常需要一個排序,並且總是需要一個段、序列項目和斷言。這些都不會使您的命令執行得更快:)