1) 错误信息很明确:SP 结束时的活跃 TX 数应与开始时的活跃 TX 数相同。
所以,当 dbo.spLogger 开始执行时,活动 TX (@@TRANCOUNT) 的数量为 1,如果我们在此 SP 中执行 ROLLBACK 语句,这将取消 ALL活跃的 TX 和 @@TRANCOUNT 变为 0 -> error/exception
2) 如果您只想避免在每个用户 SP 的每个 CATCH 块中写入 IF @@TRANCOUNT ... ROLLBACK,那么不要这样做。在ROLLBACK 之后,我会在CATCH 块内调用dbo.spLogger。
3) 如果我必须使用 TX 从其他 SP 调用 SP,那么我将使用以下模板(来源:Rusanu's blog)
create procedure [usp_my_procedure_name]
as
begin
set nocount on;
declare @trancount int;
set @trancount = @@trancount;
begin try
if @trancount = 0
begin transaction
else
save transaction usp_my_procedure_name;
-- Do the actual work here
lbexit:
if @trancount = 0
commit;
end try
begin catch
declare @error int, @message varchar(4000), @xstate int;
select @error = ERROR_NUMBER()
, @message = ERROR_MESSAGE()
, @xstate = XACT_STATE();
if @xstate = -1
rollback;
if @xstate = 1 and @trancount = 0
rollback
if @xstate = 1 and @trancount > 0
rollback transaction usp_my_procedure_name;
throw;
end catch
end
有一些小的改动:
a)SET XACT_ABORT ON
b) 仅当有@@TRANCOUNT = 0 时,我才会在CATCH 块内调用dbo.spLogger:
IF @@TRANCOUNT = 0
BEGIN
EXEC dbo.spLogger ... params ...
END
THROW -- or RAISERROR(@message, 16, @xstate)
为什么?因为如果dbo.spLogger SP 将在一个 TX 处于活动状态时将行插入到dbo.DbException 表中,那么在ROLLBACK 的情况下,SQL Server 也必须ROLLBACL 这些行。
例子:
SP1 -call-> SP2 -call-> SP3
|err/ex -> CATCH & RAISERROR (no full ROLLBACK)
<-----------
|err/ex -> CATCH & RAISERROR (no full ROLLBACK)
<-------------
|err/ex -> CATCH & FULL ROLLBACK & spLogger
4) 更新
CREATE PROC TestTx
AS
BEGIN
BEGIN TRAN -- B
ROLLBACK -- C
END
-- D
GO
-- Test
BEGIN TRAN -- A - @@TRANCOUNT = 1
EXEC dbo.TestTx
/*
Number of active TXs (@@TRANCOUNT) at the begining of SP is 1
B - @@TRANCOUNT = 2
C - @@TRANCOUNT = 0
D - Execution of SP ends. SQL Server checks & generate an err/ex
Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements. Previous count = 1, current count = 0.
*/
COMMIT -- E - Because @@TRANCOUNT is 0 this statement generates
另一个错误/ex COMMIT TRANSACTION 请求没有对应的 BEGIN TRANSACTION。
-- 测试结束
5) 见autonomous transactions: it requires SQL2008+。
自治事务本质上是一个嵌套事务,其中
内部事务不受外部状态的影响
交易。换句话说,你可以离开当前的上下文
事务(外部事务)并调用另一个事务
(自主交易)。一旦你完成了自主工作
交易,你可以回来继续在当前
交易。在自治事务中完成的事情是真正完成的
并且无论外部事务发生什么都不会改变。