更新失败后插入,事务未锁定

时间:2011-12-29 15:17:50

标签: sql-server transactions locking

我有一个包含窗口位置等用户设置的小表,只是一个名称需要唯一的简单名称=值对表:

CREATE TABLE dbo.[Settings]
(
  [Name] [nvarchar](100) NOT NULL,
  [Value] [nvarchar](4000) NOT NULL,
  CONSTRAINT [PK_Settings] PRIMARY KEY CLUSTERED
  (
    [Name] ASC
  ) ON [PRIMARY]
) ON [PRIMARY]

当我存储更改的设置时,我启动一个事务,尝试更新该值,如果更新失败(因为该记录尚不存在)我插入一条新记录:

// Begin Transaction

cmd.CommandText = "UPDATE dbo.[Settings] SET [Value] = @value WHERE [Name] = @name";
cmd.Parameters.AddWithValue("@value", value);
cmd.Parameters.AddWithValue("@name", name);
int cmdrc = cmd.ExecuteNonQuery();
if (cmdrc != 1)
{
  cmd.CommandText = "INSERT INTO dbo.[Settings] ([Name], [Value]) VALUES (@name, @value)";
  cmd.ExecuteNonQuery();
}

// Commit Transaction

我的问题:如果两个线程碰巧尝试同时存储同一个值(这种情况很少发生,但仍然存在),则会发生冲突:

两个线程都尝试更新,两个线程都获得cmdrc == 0,两个线程都尝试插入新值,只有一个会成功,另一个会得到一个SqlException。

我认为事务会锁定记录,但似乎,因为在Update语句中找不到记录,它不会被锁定,所以试图更新相同“name”值的其他线程不会被阻止,种族状况发生。

有没有办法让SQL Server锁定关键字,即使没有找到它的记录?

1 个答案:

答案 0 :(得分:1)

您可以使用

UPDATE dbo.[Settings] 
WITH (HOLDLOCK, UPDLOCK)
SET [Value] = @value WHERE [Name] = @name

所以锁定了,但为什么要这么麻烦?为什么不忽略错误,因为无论如何哪一个都会“赢”。