我有一个包含窗口位置等用户设置的小表,只是一个名称需要唯一的简单名称=值对表:
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锁定关键字,即使没有找到它的记录?
答案 0 :(得分:1)
您可以使用
UPDATE dbo.[Settings]
WITH (HOLDLOCK, UPDLOCK)
SET [Value] = @value WHERE [Name] = @name
所以锁定了,但为什么要这么麻烦?为什么不忽略错误,因为无论如何哪一个都会“赢”。