1

我认为 MERGE 是一个原子插入/更新操作,但是使用多个线程调用我的 sproc 进行 upsert 运行测试我遇到了重复的键约束违规,sproc 并不是那么困难

-- @someVal, @val1, @val2, @val3 are params passed to my sproc
-- nothing fancy going on there (with the params)
-- where MyVal is a unique non-clustered index
MERGE dbo.MyTable T
USING (@someVal [SomeVal]) S
    ON T.MyVal = S.SomeVal
WHEN MATCHED THEN 
    UPDATE
    SET A = @val1
        ,B = @val2
        ,C = @val3
WHEN NOT MATCHED THEN 
    INSERT (MyVal, A, B, C)
    VALUES (@someVal, @val1, @val2, @val3)

然而我得到了以下异常,解决这个问题的唯一方法是删除唯一索引,或者在存储过程中添加重试。

System.Data.SqlClient.SqlException (0x80131904):无法在具有唯一索引“UIX_MyUniqueConstraint”的对象“dbo.MyTable”中插入重复的键行。重复键值为 (03414D0B-15D2-4AFA-BB7F-7359BB95668A)。

在没有唯一索引的情况下进行测试时,我进行了一次欺骗检查,结果没有任何结果,确认 upsert 做了它应该做的事情,并且实际上没有插入任何欺骗

SELECT MyVal, COUNT(1)
FROM dbo.MyTable
GROUP BY MyVal
HAVING COUNT(1) > 1

这是 SQL Server 2008 R2 和以前版本中的已知错误,还是我做错了什么?

我发现这个连接问题与我的问题非常相似,似乎他们在 SQL Server 2012 中修复了它,但在以前的版本中没有

4

1 回答 1

2

查看评论后,您的问题是多线程测试应用程序。简而言之,您描述的行为是预期的。

您声明您是从一组预定义的键(someval)中随机选择的。很有可能两个不同的线程有时会选择同一个 someval 来同时运行。该值在目标表中不存在,因此线程 1 和线程 2 尝试插入它。第一个线程首先完成,第二个线程抛出错误,因为 someval 现在存在。

在这里查看答案:MERGE 是 SQL2008 中的原子语句吗?

更多关于这里的信息:http ://weblogs.sqlteam.com/dang/archive/2009/01/31/UPSERT-Race-Condition-With-MERGE.aspx

本质上,您需要更改 MERGE 语句以包含 HOLDLOCK 语句。这将强制每个合并任务在整个更新/插入过程中保持并锁定表。

MERGE INTO dbo.MyTable WITH (HOLDLOCK) AS T
...

最后,您链接到的错误实际上与此无关。

于 2013-09-16T23:28:35.697 回答