处理SQL Server并发问题

处理SQL Server并发问题,sql,sql-server,sql-server-2005,Sql,Sql Server,Sql Server 2005,有时我需要获取一个唯一的ID并将其与记录一起存储,但我无法使用标识列。因此,我有一个使用标签字段和整数提供唯一ID的表。当需要一个唯一的ID时,我调用一个存储过程并传入标签,然后它会吐出与它关联的下一个ID。 当然,在具有并发事务的环境中,这一点非常重要。也就是说,对于给定的标签,存储过程不应两次返回相同的值。我对事务隔离的有限理解导致我做了以下工作: 1) 将事务隔离级别设置为可序列化 2) 从UniqueIdTable中选择id,其中label=@inputLabel 3) 更新Unique

有时我需要获取一个唯一的ID并将其与记录一起存储,但我无法使用标识列。因此,我有一个使用标签字段和整数提供唯一ID的表。当需要一个唯一的ID时,我调用一个存储过程并传入标签,然后它会吐出与它关联的下一个ID。 当然,在具有并发事务的环境中,这一点非常重要。也就是说,对于给定的标签,存储过程不应两次返回相同的值。我对事务隔离的有限理解导致我做了以下工作:

1) 将事务隔离级别设置为可序列化

2) 从UniqueIdTable中选择id,其中label=@inputLabel

3) 更新UniqueIdTable集合id=id+1,其中label=@inputLabel

4) 返回在2)中检索到的id

但这真的安全吗?即使使用可序列化隔离,两个线程是否仍然可以并发执行(直到步骤2)?我的理解是,最高隔离级别只保证执行单个事务时不会遇到幻象行或更改来自其他线程的数据。如果是这种情况,对GetID函数的两个同时调用可能返回相同的值

我是否误解了隔离级别?我如何保证不会发生这种情况


我还有一个问题需要解决。假设我有一个表,其中有一个字段,该字段保存第二个表的外键。第一个表中的初始记录在第二个表中没有相应的记录,因此我在该字段中存储NULL。现在,某个用户运行一个操作,该操作将在第二个表中生成一条记录,并将第一个表链接到该记录。这始终是一对一的关系,因此,如果两个用户同时尝试生成记录,则会创建一条记录并链接到该记录,而另一个用户会收到一条消息,说明该记录已存在。
如何确保不在并发环境中创建副本?

我认为两个线程在步骤2中可以读取相同的值是正确的。我可以想出两个选择:

  • 在update语句中为id添加一个谓词,以便它更新 仅当值未更改时。如果更新没有更新任何 记录(不知道如何签入SQL Server,但必须是可能的) 然后重试该操作

  • 首先执行update语句。只有一个线程能够执行。然后选择更新的值

  • 我还有两个建议

  • 在单独的事务中执行此操作,以便长时间运行的事务不会阻止另一个事务

  • 在应用层保留一个线程本地块。按较大的值递增,然后是1,并使用线程本地块中的ID。这将减少服务器往返和表更新


  • 您可以使用递增获取update语句中的ID


    在一些数据库中创建一个自定义表,您可以在其中包含一个增量ID字段。任何需要此号码的应用程序都将创建一条记录并使用返回值。即使您接受此值,但不将其应用于需要它的表,即使您在一年后应用它,它仍然是唯一的。

    为什么您不能使用标识列?这就是它的目的…?是的,我永远无法完全理解“我需要功能xxx,但我无法使用内置的xxx功能…”类型的问题…看看这个“是的,我永远无法完全理解”我需要功能xxx,但我无法使用内置的xxx功能…”类型的问题“选择偏差”。如果只使用内置功能就可以解决问题,那么您就不会读到这方面的内容P在最终存储到某个表或另一个表中之前,由于各种原因,唯一ID实际上在数据库外部使用。而且有些表没有正常化,所以我需要将一个唯一的ID与多行关联。@Trent-如果我将该语句改为“output deleted.ID”,它不会在更新之前返回原始值吗?如果是这样,这正是我想要的for@Trent-会的。:)没有想到那一个。
    update UniqueIdTable
    set ID = ID + 1
    output deleted.ID
    where label = @inputLabel