sql-server – 在SQL Server 2005上违反INSERT WHERE COUNT(*)= 0的UNIQUE KEY约束

前端之家收集整理的这篇文章主要介绍了sql-server – 在SQL Server 2005上违反INSERT WHERE COUNT(*)= 0的UNIQUE KEY约束前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我从多个进程插入sql数据库。有时这些进程有时会尝试将重复的数据插入到表中。我试图以一种处理重复的方式编写查询,但是我仍然得到:
System.Data.sqlClient.sqlException: Violation of UNIQUE KEY constraint 'UK1_MyTable'. Cannot insert duplicate key in object 'dbo.MyTable'.
The statement has been terminated.

我的查询看起来像:

INSERT INTO MyTable (FieldA,FieldB,FieldC)
SELECT FieldA='AValue',FieldB='BValue',FieldC='CValue'
WHERE (SELECT COUNT(*) FROM MyTable WHERE FieldA='AValue' AND FieldB='BValue' AND FieldC='CValue' ) = 0

约束’UK1_MyConstraint’表示在MyTable中,3个字段的组合应该是唯一的。

我的问题:

为什么这不工作?
>我需要做什么修改,因为违反约束是没有异常的机会?

请注意,我知道还有其他方法解决原始问题“INSERT(如果不存在”),如(总结):

>使用TRY CATCH
> IF NOT EXIST INSERT(在具有可序列化隔离的事务中)

我应该使用其中一种方法吗?

编辑1 sql用于创建表:

CREATE TABLE [dbo].[MyTable](
  [Id] [bigint] IDENTITY(1,1) NOT NULL,[FieldA] [bigint] NOT NULL,[FieldB] [int] NOT NULL,[FieldC] [char](3) NULL,[FieldD] [float] NULL,CONSTRAINT [PK_MyTable] PRIMARY KEY NONCLUSTERED 
  (
    [Id] ASC
  )WITH (PAD_INDEX  = OFF,STATISTICS_NORECOMPUTE  = OFF,IGNORE_DUP_KEY = OFF,ALLOW_ROW_LOCKS  = ON,ALLOW_PAGE_LOCKS  = ON),CONSTRAINT [UK1_MyTable] UNIQUE NONCLUSTERED 
  (
    [FieldA] ASC,[FieldB] ASC,[FieldC] ASC
  )WITH (PAD_INDEX  = OFF,ALLOW_PAGE_LOCKS  = ON)
)

编辑2决定:

只是为了更新这个 – 我决定使用链接的问题(link)中建议的“JFDI”实现。虽然我仍然很好奇为什么原来的执行不起作用。

解决方法

为什么这不工作?

我相信sql Server的默认行为是在不再需要时释放共享锁。您的子查询将导致表上短暂的共享(S)锁定,一旦子查询完成,它将被释放。

在这一点上,没有什么可以阻止并发事务插入刚刚验证的行不存在。

我需要做什么修改,所以没有机会由于约束违规的异常?

将HOLDLOCK提示添加到子查询将指示sql Server保持锁定,直到事务完成。 (在你的情况下,这是一个隐式事务)。HOLDLOCK提示等同于SERIALIZABLE提示,它本身等同于您在“其他方法”列表中引用的可序列化事务隔离级别。

HOLDLOCK单独提示将足以保留S锁并防止并发事务插入您正在防范的行。但是,您可能会发现您的唯一键违规错误被死锁所代替,发生在相同的频率。

如果您仅在表上保留S锁,请考虑在两次同时尝试插入同一行(在锁步骤中进行)之间的竞争 – 两者都能成功获取表上的S锁,但两者都不能成功获取Exclusive (X)锁需要执行插入。

幸运的是,这个确切情况有另一种锁类型,称为Update(U)锁。 U锁与S锁相同,具有以下区别:当同一资源同时保持多个S锁时,一次只能保持一个U锁。 (换句话说,虽然S锁相互兼容(即可以共存而没有冲突),U锁不能相互兼容,但可以与S锁共存;进一步沿着频谱,Exclusive(X)锁不是兼容S或U锁)

您可以使用UPDLOCK提示将子查询中的隐式S锁升级到U锁。

现在将在初始select语句中插入同一行的两个并发尝试序列化,因为它获取(并保存)与并发插入尝试的另一个U锁不兼容的U锁。

NULL值

FieldC允许NULL值的事实可能会产生一个单独的问题。

如果ANSI_NULLS是(默认),则等于’FieldC = NULL’将返回false,即使FieldC为NULL(在ANSI_NULLS打开时必须使用IS NULL运算符来检查null)。由于FieldC为空,因此当您插入NULL值时,您的重复检查将无法正常工作。

要正确处理null,您将需要修改EXISTS子查询以使用IS NULL运算符,而不是=当插入值为NULL时。 (或者您可以更改表以禁止所有相关列中的NULL)。

sql Server联机丛书参考

> Locking Hints
> Lock Compatibility Matrix
> ANSI_NULLS

猜你在找的MsSQL相关文章