SQL Server MERGE命令重复pk错误

Posted

技术标签:

【中文标题】SQL Server MERGE命令重复pk错误【英文标题】:SQL Server MERGE command duplicate pk error 【发布时间】:2017-10-31 08:56:41 【问题描述】:

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

我正在尝试执行插入语句,如果记录存在具有相同的键(字段、名称),则改为更新它。

性能很重要,所以我尽量避免使用临时表解决方案。

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);

编辑:使用可序列化/锁定 24 小时进行测试。 30 分钟后:没有错误。

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

【问题讨论】:

【参考方案1】:

您必须提高事务隔离级别。 设置事务隔离级别可序列化

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

可序列化的实现

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

该策略还不足以提供技术保证 完全可序列化,因为新的或更改的数据可能出现在 事务先前处理的行范围。这 并发现象被称为幻象,可能导致 在任何系列时间表中都不会发生的效果。

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

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

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

【讨论】:

必须夸大了它。我的偏好是将HOLDLOCK 提示添加到特定语句中。 HOLDLOCK 会给出相同的结果 所以这实际上是一样的吗? MERGE Fielddata WITH HOLDLOCK 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); "HOLDLOCK 有点被弃用了,你应该改用 SERILIZABLE。"这是真的? social.msdn.microsoft.com/Forums/sqlserver/en-US/… @LochNess - 它未被列为已弃用。如果您愿意,可以使用MERGE Fielddata WITH (SERIALIZABLE) 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);,因为它们是等价的。

以上是关于SQL Server MERGE命令重复pk错误的主要内容,如果未能解决你的问题,请参考以下文章

Sql server的Merge语句,源表中如果有重复数据会导致执行报错

SQL Server2008中的MERGE SQL语句中的MERGE的全称是什麼?代表什麼意思? 有没有

SQL 合并错误:MERGE 语句试图更新或删除 [重复]

SQL Merge输出匹配和不匹配的结果

SQL Server 错误:用户登录失败 -- 在迁移命令更新数据库 [重复]

在SQL SERVER 中对表不能设置主键