有时我需要获取一个唯一的ID并将其与记录一起存储,但我无法使用标识列。所以我有一个表,它使用标签字段和整数提供唯一的ID。当需要一个唯一的ID时,我调用一个存储过程并传入标签,然后它会发出与之关联的下一个ID。 当然,在具有并发事务的环境中保持可靠性非常重要。也就是说,存储过程永远不应该为给定标签返回两次相同的值。我对事务隔离的理解有限,这使我做了以下事情:
1)将事务隔离级别设置为可序列化
2)SELECT id FROM UniqueIdTable WHERE label = @inputLabel
3)UPDATE UniqueIdTable SET id = id + 1 WHERE label = @inputLabel
4)返回2)
中检索到的id但这实际上是否安全?即使可序列化隔离,两个线程是否仍然可以同时执行到第2步)?我的理解是,最高隔离级别仅保证单个事务将在不经历幻像行或从其他线程更改数据的情况下执行。如果是这种情况,两次同时调用GetID函数可能会返回相同的值。
我是否误解了隔离级别的问题?我如何保证不会发生这种情况?
我还有另一个需要解决的问题。假设我有一个表中有一个字段,其中包含第二个表的外键。最初第一个表中的记录在第二个表中没有相应的记录,因此我在该字段中存储NULL。现在,在某个时刻,用户运行一个操作,该操作将在第二个表中生成一个记录,并将第一个表链接到该表。这始终是一对一的关系,因此如果两个用户同时尝试生成记录,则会创建并链接到单个记录,而另一个用户会收到一条消息,指出该记录已存在。 如何确保不在并发环境中创建重复项?
答案 0 :(得分:1)
您可以使用output增加和在更新语句中获取ID。
update UniqueIdTable
set ID = ID + 1
output deleted.ID
where label = @inputLabel
答案 1 :(得分:0)
我认为你是正确的,两个线程可以在步骤2中读取相同的值。我可以想到两个选择:
在update语句中为id添加谓词,以便更新 只有当值没有改变时。如果更新没有更新任何 记录(不知道如何检查SQL Server但必须可以) 然后重试该操作。
首先执行更新语句。只有一个线程可以执行。然后选择更新的值。
我还有其他两条建议
在单独的事务中执行此操作,以便长时间运行的事务不会阻止另一个事务
在应用程序层保留一个线程局部块。按值增加大于1,并使用线程局部块中的id。这将减少服务器往返和表的更新
答案 2 :(得分:0)
在可以包含增量ID字段的某个数据库中创建自定义表。任何需要此编号的应用都会创建一条记录并使用返回的值。即使您使用此值并且不将其应用于您需要的表格,即使您在一年后申请,它仍然是唯一的。