我有一个简单的SP,它可以执行INSERT或UPDATE,具体取决于表中是否存在数据。
CREATE PROCEDURE [dbo].spUpsert
-- Parameters to Update / Insert a StudentSet
@StudentSetId nvarchar(128),
@Status_Id int
AS
BEGIN
BEGIN TRY
BEGIN TRANSACTION
SET XACT_ABORT ON;
SET NOCOUNT ON;
IF EXISTS(SELECT StudentSetId FROM StudentSet WHERE StudentSetId = @StudentSetId)
BEGIN
UPDATE StudentSet SET ModifiedDate = GETDATE(), Status_Id = @Status_Id
WHERE StudentSetId = @StudentSetId;
END
ELSE
BEGIN
INSERT INTO StudentSet
(StudentSetId, Status_Id)
VALUES
(
@StudentSetId,
@Status_Id
)
END
COMMIT TRANSACTION
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION
END CATCH
END
写了一个像这样的方法:
public void Upsert(string studentSetId, int statusId)
{
this.DatabaseJobs.ExecuteSqlCommand(@"exec spUpsert
@StudentSetId = {0},
@Status_Id = {10} ",
studentSetId,
statusId);
}
以下是如何使用它的: 学生有一个文件,准确地说是一个xml,它被发送到处理器,该处理器将该SP称为该过程的一部分。可以上传多个文件,处理器设计用于生成5个线程的5个文件。
对于一批5个文件,它会抛出此错误:
EXECUTE之后的事务计数表示BEGIN和COMMIT语句的数量不匹配。先前count = 1,当前计数= 0. EXECUTE之后的事务计数表示BEGIN和COMMIT语句的数量不匹配。先前的计数= 1,当前计数= 0。
数字5不是完美的,当上传超过5个文件时可能会发生。比我还没试过的还要少。
所以我搜索并找到了一个实现@@ TRANCOUNT详细here&的使用的解决方案。 here
@@ TRANCOUNT是一个全局变量,文章中建议的用法似乎就像会话的本地变量一样。我的意思是SQL Server中的任何进程都可以增加@TRANCOUNT并依赖它可能不会产生预期的结果。
我的问题是处理这类情况的好方法是什么?
提前致谢。
答案 0 :(得分:2)
首先,@@TRANCOUNT
是信息性的 - 它告诉您当前线程当前正在进行多少嵌套事务。在您的情况下,当调用存储过程时,事务已在进行中,因此事务计数为1。
您的问题是ROLLBACK
回滚所有事务,包括任何嵌套事务。如果你想中止整个批次,这正是你想要的,错误只是告诉你它已经发生了。
但是,如果您只想回滚在本地创建的事务,则必须执行稍微不同的操作。你必须在开始时保存事务,然后在出错时你可以回滚到那一点(在任何工作完成之前),然后提交它(没有完成工作)。
BEGIN TRAN
DECLARE @savepoint varbinary(16) set @savepoint = newid()
SAVE TRAN @savepoint
BEGIN TRY
-- Do some stuff here
select 1/0; -- divide by zero error
COMMIT TRAN
END TRY
BEGIN CATCH
ROLLBACK TRAN @savepoint;
COMMIT TRAN -- important!!!
--re-raise the error if you want (or recover in some other way)
RAISERROR('Rethrowing error', ERROR_SEVERITY(), ERROR_STATE() );
END CATCH
答案 1 :(得分:0)
好吧,如果事务是在.NET代码中启动的,那么如果它回滚到相同的代码中会很好。但是,如果不可能,那么您应该检查@@ TRANCOUNT。
然而,你遗漏了一件重要的事情:如果交易没有开始怎么办?您的代码构造方式需要事务处理。如果您(或其他人)从SSMS执行程序怎么办?
我建议你做以下事情:
修改强>
当然,正如Ben在他的回答中所说,你可以保存交易而不是在代码中开始。例如,如果存在事务,则将其保存以便能够仅将部件从SAVE回滚到ROLLBACK。如果没有交易,请在您的程序中启动它。
Remus Rusanu有很好的template。