【问题标题】:Primary key violation error in sql server 2008sql server 2008 中的主键冲突错误
【发布时间】:2013-03-25 04:22:46
【问题描述】:

我在 C# 中创建了两个线程,并且我正在并行调用两个单独的函数。这两个函数都从 XYZ 表中读取最后一个 ID 并插入值为 ID+1 的新记录。这里 ID 列是主键。当我执行这两个函数时,我收到主键违规错误。这两个函数都有以下查询:

insert into XYZ values((SELECT max(ID)+1 from XYZ),'Name') 

似乎两个函数都在一次读取值并尝试插入相同的值。 我该如何解决这个问题..?

【问题讨论】:

  • 嗯,Java 有一个 synchronized 关键字来防止多个线程相互干扰。我建议你在 C# 中查找类似结构的一些多线程链接
  • @ChetterHummin 不,他的问题是他试图找出要在应用程序中插入的 ID,而他应该让数据库处理它。是的,他也可以创建一个共享和同步的 ID 变量,但这不会阻止数据库同步的问题。例如,考虑第三方向他的表中插入不使用他的共享变量的问题。然后他还是有同样的问题。
  • @KyleM 感谢您的澄清!

标签: c# sql-server sql-server-2008


【解决方案1】:

让数据库为您选择 ID。从上面的代码中可以明显看出,您真正想要的是一个自动递增的整数 ID 列,数据库绝对可以为您处理。所以正确设置你的表,而不是你当前的插入语句,这样做:

insert into XYZ values('Name') 

如果你的数据库表已经设置好了,我相信你可以发出类似这样的语句:

alter table your_table modify column you_table_id int(size) auto_increment

最后,如果无论出于何种原因,这些解决方案都不适用(包括,如您在 cmets 部分中指出的,无法编辑表架构),那么您可以按照 cmets 中建议的其他用户之一进行操作并创建一个找到下一个 ID 的同步方法。您基本上只需创建一个返回 int 的静态方法,在该静态方法中发出您的 select id 语句,然后使用返回的结果将您的下一条记录插入到表中。由于此方法不能保证成功插入(由于外部应用程序也可以插入同一个表中),因此您还必须捕获异常并在失败时重试)。

【讨论】:

  • 以上查询就是一个例子。实际上它的表超过 50 个字段和主键字段不是自动递增的。我们必须明确插入唯一值。
  • @user1365604 那么也许你应该解释一下你的主键是如何工作的。从您到目前为止所解释的内容来看,它肯定应该是一个增量列。
  • @user1365604 为什么必须显式插入?为什么不能更正数据库表的设计以自动递增?你知道什么是'alter'语句吗?
  • 上面的查询(alter table XYZ modify column ID int auto_increment)给出了错误('modify'附近的语法不正确。)
  • 我正在考虑在任何线程读取最后一个值并插入新记录时锁定表。它会帮助我解决这个问题。如果是,我该怎么做。
【解决方案2】:

将 ID 列设置为“身份”列。然后,您可以按以下方式执行查询:

insert into XYZ values('Name') 

我认为您不能在创建列后使用 ALTER TABLE 将列更改为 Identity。使用 Managament Studio 将此列设置为 Identity。如果您的表有很多行,这可能是一个长时间运行的过程,因为它实际上会将您的数据复制到新表(将执行表重新创建)。

该选项很可能在您的 Managament Studio 中被禁用。为了启用它,打开工具->选项->设计器并取消选中选项“防止保存需要重新创建表的更改”...根据您的表大小,您可能还必须设置超时。在此期间您的桌子将被锁定。

【讨论】:

    【解决方案3】:

    此类问题的解决方案是使用某种序列生成 ID。

    例如,在 SQL Server 中,您可以使用以下命令创建序列:

    CREATE SEQUENCE Test.CountBy1
        START WITH 1
        INCREMENT BY 1 ;
    GO
    

    然后在 C# 中,您可以从 Test 中检索下一个值,并在插入之前将其分配给 ID。

    【讨论】:

    【解决方案4】:

    听起来您想要更高的transaction isolation level 或更严格的locking

    我不经常使用这些功能,所以如果我错了,希望有人会提出修改建议,但你想要其中之一:

    -- specify the strictest isolation level
    SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
    insert into XYZ values((SELECT max(ID)+1 from XYZ),'Name') 
    

    -- make locks exclusive so other transactions cannot access the same rows
    insert into XYZ values((SELECT max(ID)+1 from XYZ WITH (XLOCK)),'Name') 
    

    【讨论】:

    • 我不明白反对意见,更高的事务隔离级别可以帮助解决这个问题。虽然很可能使用IDENTITY 是解决问题的更好方法。
    • 我认为有一个隔离级别叫做 ReReadable。我必须检查 MSDN,但我认为身份列会更好
    • @Greg:只需单击我的答案中指向“事务隔离级别”的链接,即可查看可用的级别。您是对的,身份列会更好,但是已经有 2 个其他答案暗示了这一点。此外,标识列仅在您的列是整数并且您可以控制架构并且您永远不必将列设置为特定值时才有效。
    • 对不起,完全错过了链接。可重复阅读是我的想法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多