处理并发环境中的max(ID)

时间:2011-09-24 15:36:14

标签: sql-server sql-server-2005

我是Web应用程序编程的新手,并且使用像SQL Server这样的RDBMS处理并发。我正在使用SQL Server 2005 Express Edition。

我正在生成员工代码,其中最后四位数来自此查询:

SELECT max(ID) FROM employees WHERE district = "XYZ";

我没有关注如何处理因并发连接而可能出现的问题。许多用户可以选择相同的最大值(ID),当一个用户点击“保存记录”时,该ID可能已被其他用户占用。

如何处理这个问题?

3 个答案:

答案 0 :(得分:2)

这有两种方法可以做你想做的事。您最终可能会在EmpCode上遇到唯一约束违规的事实我会让您担心:)。

1。使用scope_identity()获取最后插入的ID并使用它来计算EmpCode

表格定义:

create table Employees
(
  ID int identity primary key,
  Created datetime not null default getdate(),
  DistrictCode char(2) not null,
  EmpCode char(10) not null default left(newid(), 10) unique
)

向Employees添加一行。应该在交易中完成,以确保您不会使用left(newid(), 10)EmpCode的默认随机值:

declare @ID int

insert into Employees (DistrictCode) values ('AB')

set @ID = scope_identity()

update Employees
set EmpCode = cast(year(Created) as char(4))+DistrictCode+right(10000+@ID, 4)
where ID = @ID 

2。EmpCode成为computed column

表格定义:

create table Employees
(
  ID int identity primary key,
  Created datetime not null default getdate(),
  DistrictCode char(2) not null,
  EmpCode as cast(year(Created) as char(4))+DistrictCode+right(10000+ID, 4) unique
)

向员工添加一行:

insert into Employees (DistrictCode) values ('AB')

答案 1 :(得分:2)

使用MAX是一个坏主意,因为使用适当的锁定机制,您将无法在同一区域的多个线程中插入行。 如果您可以一次只创建一个用户,并且如果您的测试表明即使每个区域有大量用户,MAX也可以扩展,那么可以使用它。 长话短说,尽可能地处理身份,你应该依赖IDENTITY。真。

但如果不可能,一种解决方案是在单独的表中处理ID。

Create Table DistrictID (
    DistrictCode char(2),
    LastID Int,
    Constraint PK_DistrictCode Primary Key Clustered (DistrictCode)
);

然后递增LastID计数器。如果要在并行线程中创建许多用户,则递增ID是一个与用户创建事务分离的事务,这一点很重要。您可以限制只按顺序生成ID。

代码可能如下所示:

Create Procedure usp_GetNewId(@DistrictCode char(2), @NewId Int Output)
As

Set NoCount On;
Set Transaction Isolation Level Repeatable Read;
Begin Tran;
Select @NewId = LastID From DistrictID With (XLock) Where DistrictCode = @DistrictCode;
Update DistrictID Set LastID = LastID + 1 Where DistrictCode = @DistrictCode;
Commit Tran;

可重复读取 XLOCK 关键字是避免两个线程获取相同ID所需的最小值。 如果表格没有所有区域,则需要将可重复读取更改为可序列化,并将更新更改为插入

答案 2 :(得分:1)

这可以通过Transaction Isolation Levels来完成。例如,如果您将SERIALIZABLE指定为级别,那么将阻止其他事务,以便您不会遇到此问题。

如果我没有正确理解你的问题,请告诉我。