使用@@TRANCOUNT有用吗?
本文关键字:有用 @@TRANCOUNT 使用 | 更新日期: 2023-09-27 18:03:16
我有一个简单的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后的Transaction count表示BEGIN和COMMIT语句的数目不匹配。前一个计数= 1,当前计数= 0。EXECUTE后的事务计数表示BEGIN和COMMIT语句的数目不匹配。前一个计数= 1,当前计数= 0。
数字5不是一个完美的数字,当上传的文件超过5个时可能会发生这种情况。更少,我没试过。
所以我搜索并找到了一个解决方案,实现了@@TRANCOUNT的使用,详细说明在这里&
@@TRANCOUNT是一个全局变量,正如文章中建议的那样,它的用法似乎是会话的本地变量。我的意思是,SQL Server中的任何进程都可以增加@TRANCOUNT,依赖于此可能不会产生预期的结果。
我的问题是处理这种情况的好方法是什么?首先,@@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
嗯,如果事务是在。net代码中启动的,那么如果它在相同的代码中回滚就好了。但是,如果不可能,那么您应该检查@@TRANCOUNT。
但是,您忽略了一件重要的事情:如果事务根本没有启动怎么办?您的代码是以需要事务的方式构造的。如果您(或其他人)从SSMS执行过程怎么办?
我建议你这样做:
- 在代码开头本地存储@@trancount(声明@mytrancount)
- 在你开始处理之前,检查@mytrancount,如果没有交易,启动一个
- 最后提交事务,但在提交前再次检查@mytrancount
编辑
当然,正如Ben在他的回答中所说,您可以保存事务,而不是在代码中开始。例如,如果有一个事务,保存它,以便能够只回滚从save到ROLLBACK的部分。如果没有交易,在你的程序中启动它。
Remus Rusanu有一个很好的模板。