我有一个存储过程,它以@id
作为输入参数。表格Student
在Id_Column
和Count_Column
上有主键。如果表中存在给定id
的任何记录,那么我从表中选择最大Count_Column
并通过将max Count_Column
递增1而使用零值来插入新行。
我在WCF服务中从ado.net代码调用此存储过程,并且从asp.net Web应用程序调用此服务。
这个存储过程在正常情况下工作正常,但是当多个用户同时调用它时,会发生主键冲突问题,同样的情况我也通过使应用程序多线程重现。我知道这个问题与并发有关,最初我在select查询中使用with(nolock)
,但我现在已经删除了它。
某处我已经读过,通过设置事务隔离级别,它可以解决,但是当我尝试时,我有些得到回滚事务异常。
请告诉我这个问题的任何有效解决方案。
declare @iCount = 0;
if exists(select 'x' from Student with(nolock) where Id_Column = @iId)
begin
set @iCount = (select max(Count_Column)
from Student
where Id_Column = @iId)
end
insert into Student
values(@id, @iCount + 1);
第二个解决方案:
begin try
set transaction isolation level serializable
begin transaction
declare @iCount = 0;
if exists(select 'x' from from Student with(nolock) where Id_Column = @iId)
begin
set @iCount = (select max(Count_Column)
from Student
where Id_Column = @iId)
end
insert into Student
values(@id, @iCount + 1);
commit transaction
end try
begin catch
rollback transaction
end catch
答案 0 :(得分:3)
尝试类似......
BEGIN TRY
BEGIN TRANSACTION;
DECLARE @iCount INT;
IF EXISTS(SELECT 1 FROM Student WITH(UPDLOCK,HOLDLOCK) WHERE Id_Column = @iId)
BEGIN
select @iCount = ISNULL(max(Count_Column), 0) + 1
from Student WITH(UPDLOCK,HOLDLOCK) where Id_Column = @iId
insert into Student
values(@id, @iCount);
END
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF (@@TRANCOUNT <> 0)
ROLLBACK TRANSACTION;
END CATCH
重要提示
您应该在此处使用Identity
列来处理自动增量值。如果您使用的是sql server 2012或更高版本,您还可以选择使用Sequence Object
自动增量。
答案 1 :(得分:0)
有一个故障安全策略:SERIALIZABLE
隔离并在发生死锁时重试。 SERIALIZABLE
的定义是as-if-serial执行,它排除了所有竞争条件。
我不知道为什么你会在那里捕捉异常。所有这一切都是为了抑制错误。
答案 2 :(得分:0)
Oracle有序列,MS跟着Sql Server 2012,其实现调用序列对象,而不是特定于表。
CREATE SEQUENCE
Schema.MySequence
AS int
INCREMENT BY 1 ;
然后您可以按照以下方式使用它:
DECLARE @iCount int ;
SET @iCount = NEXT VALUE
FOR Schema.MySequence;
这与Oracle中的nextval类似,将确保唯一性。唯一的问题是,如果回滚/失败的交易,你是否会接受计数序列中的差距......