Sql-Server

這三個版本的 TSQL 片段有什麼區別?

  • September 3, 2022

版本 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 模式和反模式。

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