Sql-Server
這三個版本的 TSQL 片段有什麼區別?
版本 1
DECLARE @key INTEGER = 33, @val INTEGER = 44; BEGIN TRANSACTION; INSERT dbo.t([key], val) SELECT @key, @val WHERE NOT EXISTS ( SELECT 1 FROM dbo.t WITH (UPDLOCK, SERIALIZABLE) WHERE [key] = @key ); IF @@ROWCOUNT = 0 BEGIN UPDATE dbo.t SET val = @val WHERE [key] = @key; END COMMIT TRANSACTION;
版本 2
DECLARE @key INTEGER = 33, @val INTEGER = 44; BEGIN TRANSACTION; INSERT dbo.t WITH (UPDLOCK, SERIALIZABLE) ([key], val) SELECT @key, @val WHERE NOT EXISTS ( SELECT 1 FROM dbo.t WHERE [key] = @key ); IF @@ROWCOUNT = 0 BEGIN UPDATE dbo.t SET val = @val WHERE [key] = @key; END COMMIT TRANSACTION;
版本 3
DECLARE @key INTEGER = 33, @val INTEGER = 44; BEGIN TRANSACTION; INSERT dbo.t WITH (UPDLOCK, SERIALIZABLE) ([key], val) SELECT @key, @val WHERE NOT EXISTS ( SELECT 1 FROM dbo.t WITH (UPDLOCK, SERIALIZABLE) WHERE [key] = @key ); IF @@ROWCOUNT = 0 BEGIN UPDATE dbo.t SET val = @val WHERE [key] = @key; END COMMIT TRANSACTION;
我對提示的位置感到困惑。如果
INSERT
部分中的表和子查詢相同,那麼在哪裡寫提示仍然有區別嗎?
提示僅適用於它們所在的位置。源表和目標表是否相同並不重要。
需要提示
NOT EXISTS
以確保 (a) 行保持鎖定足夠長的時間;(b) 如果測試範圍內不存在行,則在事務期間繼續存在這種情況。在存在測試中提示讀取是實現這些目標的最可靠方法。版本 1 是正確的“upsert”模式之一,用於預期插入更常見的地方。
版本 2 缺少表讀取的必要提示,無法在並發下正確工作,同時最大限度地減少死鎖。
NOT EXISTS
在插入發生之前,另一個會話在範圍內插入一行的機會很小。版本 3 不必要地複制了插入目標上的提示,但它在其他方面是無害的。
請參閱Michael Swart 的SQL Server UPSERT 模式和反模式。