我认为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)
然而我得到以下异常,解决这个问题的唯一方法是删除唯一索引,或者在sproc中添加重试。
System.Data.SqlClient.SqlException(0x80131904):无法插入 具有唯一索引的对象'dbo.MyTable'中的重复键行 'UIX_MyUniqueConstraint'。重复的键值是 (03414D0B-15D2-4AFA-BB7F-7359BB95668A)。
在没有唯一索引的情况下进行测试时,我进行了一次欺骗检查,但没有变成任何东西,确认upsert做了它应该做的事情并且没有插入任何欺骗
SELECT MyVal, COUNT(1)
FROM dbo.MyTable
GROUP BY MyVal
HAVING COUNT(1) > 1
这是SQL Server 2008 R2和先前版本中的已知错误,还是我做错了什么?
我发现这个connect issue与我的问题非常相似,看起来他们在SQL Server 2012中修复了它,但在之前的版本中没有修复
答案 0 :(得分:2)
在审核评论后,您的问题是多线程测试应用。简而言之,您所描述的行为是预期的。
您声明您是从一组预定义的键(someval)中随机挑选的。两个不同的线程很可能有时选择相同的someval来同时运行。该值在目标表中不存在,因此线程1和线程2尝试插入它。第一个线程首先完成,第二个线程抛出错误,因为someval现在存在。
请在此处查看答案:Is MERGE an atomic statement in 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
...
最后,您链接的错误实际上与此无关。