TSQL序列号生成

时间:2016-07-09 20:00:41

标签: sql-server tsql

我有一个应用程序,当一个新用户被添加到某个位置时,会为其分配一个序号。因此,例如,位置01的第一个用户将被分配01-0001,第二个用户将被分配01-0002等。

尽管我很容易找到该位置和+1的最大用户数,但我的问题是我需要这个是线程/冲突安全的。

虽然它并不常见,但我不希望一个查询找到max()号,而另一个查询正在同一时刻添加该号码。 (它发生在我原来的应用程序之前,虽然在5年内只发生过两次。)

最好的方法是什么?我宁愿不依赖于一个独特的约束,因为它会抛出错误并迫使进程再次尝试。

2 个答案:

答案 0 :(得分:4)

您可以使用

BEGIN TRAN

SELECT @UserId = MAX(UserId)
FROM YourTable WITH(UPDLOCK, HOLDLOCK, ROWLOCK)
WHERE LocationId = @LocationId

--TODO: Increment it or initialise it if no users yet.

INSERT INTO YourTable (UserId, Name)
VALUES (@UserId, @Name)

COMMIT

一次只有一个会话可以对资源进行更新锁定,因此如果两个并发会话尝试为同一位置插入一行,则其中一个将被阻塞,直到另一个会话提交为止。 HOLDLOCK是给出可序列化的语义并阻止包含最大值的范围

这是一个潜在的瓶颈,但这是设计因为要求数字是连续的。性能更好的替代方案,例如IDENTITY,并不能保证这一点。实际上虽然听起来好像你的应用程序使用率很低,但这可能不是一个大问题。

另一个可能的问题是,如果某个位置的当前最大值的用户被删除,那么该ID将被回收,但这对于现有应用程序的声音是相同的,所以我认为这不是问题或者只是没有发生。

答案 1 :(得分:0)

您可以使用sequence描述的create sequence dbo.UserId as int start with 1 increment by 1; 对象。 创建一个新的序列非常简单,例如你可以使用这个代码

sequence

使用NEXT VALUE FOR对象,您无需了解任何碰撞。每次使用select next value for dbo.UserId; 语句获取时,Sequence将始终返回下一个值,就像在此代码中一样

SELECT

即使您的回滚事务或即使您在单独的并行事务中获得下一个值,下一个值也将正确返回。