SQL Server MERGE命令重复pk错误

时间:2017-10-31 08:56:42

标签: sql-server tsql

如果事务同时发生,则此存储过程通常会失败,因为它违反了重复键(字段,名称)上的pk约束。我一直在尝试, holdlock, rowlock和begin / commit transaction,这会导致同样的错误。

我正在尝试执行insert语句,如果记录中存在相同的键(字段,名称),请更新它。

性能很重要,所以我试图避免临时表解决方案。

MERGE Fielddata AS TARGET
USING (VALUES (@Field, @Name, @Value, @File, @Type))
    AS SOURCE (Field, Name, Value, File, Type)
    ON TARGET.Field = @Field AND TARGET.Name = @Name
WHEN MATCHED THEN
    UPDATE
    SET Value = SOURCE.Value,
        File = SOURCE.File,
        Type = SOURCE.Type
WHEN NOT MATCHED THEN
    INSERT (Field, Name, Value, File, Type)
    VALUES (SOURCE.Field, SOURCE.Name, SOURCE.Value, SOURCE.File, SOURCE.Type);

编辑:使用serializable / holdlock进行24小时测试。 30分钟后:没有错误。

编辑2:WITH (SERIALIZABLE) / SET TRANSACTION ISOLATION LEVEL SERIALIZABLE有效地解决了重复的关键问题,在我们的案例/场景中花费了一点点性能。

1 个答案:

答案 0 :(得分:2)

您必须提高事务隔离级别。 设置交易隔离级别可串行化

  

SERIALIZABLE指定以下内容:+语句无法读取数据   已被修改但尚未由其他交易承诺的。没有   其他事务可以修改当前读取的数据   事务直到当前事务完成。 其他   事务无法插入具有可能落入的键值的新行   当前事务中任何语句读取的键范围   直到当前事务完成。范围锁定放在   与每个语句的搜索条件匹配的键值范围   在交易中执行。这阻止了其他交易   更新或插入任何符合任何条件的行   当前事务执行的语句。这意味着,如果有的话   它们是第二次执行事务中的语句   将读取相同的行集。范围锁保持到   交易完成。这是最严格的隔离   级别,因为它锁定整个键范围并保持锁定   直到交易完成。因为并发性较低,请使用   此选项仅在必要时使用。 此选项与效果相同   在a中所有SELECT语句中的所有表上设置HOLDLOCK   事务。

可序列化的实现

  

SQL Server碰巧使用了serializable的锁定实现   隔离级别,物理锁被获取并保持到最后   的事务(因此不推荐的表提示HOLDLOCK为   SERIALIZABLE的同义词。

     

这种策略还不足以提供技术保障   完全可串行化,因为新的或更改的数据可能出现在   事务先前处理的行范围。这个   并发现象被称为幻像,并且可以导致   任何连续计划中都不会发生的影响。

     

为了确保防止幻像并发现象,锁定   SQL Server在可序列化隔离级别上采取的也可能   合并键范围锁定以防止新行或更改行   出现在先前检查的索引键值之间。范围锁   并不总是在可序列化的隔离级别下获得;我们所有人   可以说一般来说SQL Server总是获得足够的锁   满足可序列化隔离级别的逻辑要求。   事实上,锁定实现经常获得更多,并且   比确保可串行化所需的更严格的锁定,   但我离题了。

https://sqlperformance.com/2014/04/t-sql-queries/the-serializable-isolation-level

如果简单:它不仅会阻止来源,还会阻止插入范围