Sql Merge语句在多线程时导致重复

时间:2014-05-07 05:40:41

标签: sql sql-server multithreading

我有一个合并语句,它根据键执行upsert。 (为简化起见,我们将其称为RefId)。这是为了确保表格RefId是唯一的。 但是,在生产中,我们有多个服务器使用此存储过程插入到此表中,如果两个服务器以非常接近的间隔使用相同的RefId插入,则会发生重复(即2次插入)而不是1次插入和更新。我相信这是因为,SQL服务器锁定新插入的行,另一个并行调用存储过程无法检测到它的存在。 MERGE语句不支持NoLock,因此我看不到明显的解决方法。  我已经使用多个线程(而不是服务器)模拟了并行插入,并且在这种情况下也会发生偶然的重复。除了在数据库上强制执行一个唯一约束之外(我不能出于无法控制的原因),有什么方法可以让我的upsert在并发情况下按预期工作?

这是存储过程(当使用合理的时序差异进行插入更新时,它可正常工作,并行情况下失败)

WITH UniqueData AS 
    ( SELECT * FROM 
      ( SELECT *, rank() over ( PARTITION BY RefId ) AS UniqueRank 
          FROM @Data  
      ) AS Ranked WHERE UniqueRank=1 
    )
  MERGE MyTable AS destination
  USING UniqueData AS source
  ON ( destination.RefId = source.RefId)
  WHEN NOT MATCHED THEN
    INSERT (RefId, Miles,UpdateUTC)
    VALUES( source.RefId, source.Miles, getutcdate())
  WHEN MATCHED THEN
    UPDATE SET 
               destination.Miles = ISNULL(source.Miles , destination.Miles ),
               destination.UpdateUTC = getutcdate()   

1 个答案:

答案 0 :(得分:3)

您可以使用WITH(HOLDLOCK)

...
 MERGE MyTable WITH (HOLDLOCK) AS destination
...

你可以在这里阅读详细信息: http://weblogs.sqlteam.com/dang/archive/2009/01/31/UPSERT-Race-Condition-With-MERGE.aspx