合并声明重复密钥错误

时间:2013-09-13 22:52:13

标签: sql-server tsql

我认为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中修复了它,但在之前的版本中没有修复

1 个答案:

答案 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
...

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