SQL Server - 存储过程中的嵌套事务

Shi*_*aru 9 transactions nested sql-server-2008

让我们说这是情况:

  [Stored Proc 1]
  BEGIN
     BEGIN TRANSACTION
       ...
            exec sp 2   
     COMMIT
  END
Run Code Online (Sandbox Code Playgroud)

现在,如果SP 2 - 由于某种原因回滚,SP 1 - 提交或回滚还是抛出异常?

谢谢.

Aar*_*and 8

SQL Server中没有自治事务.您可能会看到@@TRANCOUNT增加超过1,但回滚会影响整个事情.

编辑要求指出文件.不知道明确记录这个主题的主题,但我可以在行动中向您展示.

USE tempdb;
GO
Run Code Online (Sandbox Code Playgroud)

内部过程:

CREATE PROCEDURE dbo.sp2
    @trip BIT
AS
BEGIN
    SET NOCOUNT ON;

    BEGIN TRANSACTION;

    PRINT @@TRANCOUNT;

    IF @trip = 1
    BEGIN
        IF @@TRANCOUNT > 0
            ROLLBACK TRANSACTION;
    END
    ELSE
    BEGIN   
        IF @@TRANCOUNT > 0
            COMMIT TRANSACTION;
    END

    PRINT @@TRANCOUNT;
END
GO
Run Code Online (Sandbox Code Playgroud)

外部过程:

CREATE PROCEDURE dbo.sp1
    @trip BIT
AS
BEGIN
    SET NOCOUNT ON;

    BEGIN TRANSACTION;

    PRINT @@TRANCOUNT;

    BEGIN TRY
        EXEC dbo.sp2 @trip = @trip;
    END TRY
    BEGIN CATCH
        PRINT ERROR_MESSAGE();
    END CATCH

    PRINT @@TRANCOUNT;

    IF @@TRANCOUNT > 0
        COMMIT TRANSACTION;

    PRINT @@TRANCOUNT;
END
GO
Run Code Online (Sandbox Code Playgroud)

所以现在让我们调用它,让一切都提交:

EXEC dbo.sp1 @trip = 0;
Run Code Online (Sandbox Code Playgroud)

结果:

1
2
1
1
0

现在让我们调用它并回滚内部过程:

EXEC dbo.sp1 @trip = 1;
Run Code Online (Sandbox Code Playgroud)

结果:

1
2
0 < - 请注意,此处的回滚
在EXECUTE表示BEGIN和COMMIT语句的数量不匹配后回滚两个 事务计数.先前的计数= 1,当前计数= 0
0
0


Rem*_*anu 7

可以回滚SP2完成的工作,而不会松开SP1完成的工作.但要实现这一点,您必须使用非常特定的模式编写存储过程,如异常处理和嵌套事务中所述:

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;

        raiserror ('usp_my_procedure_name: %d: %s', 16, 1, @error, @message) ;
    end catch   
end
Run Code Online (Sandbox Code Playgroud)

并非所有错误都是可恢复的,事务无法从中恢复多个错误条件,最明显的例子是死锁(在事务已经回滚,会通知您死锁异常).必须使用此模式编写SP1和SP @.如果你有一个流氓SP,或者你想简单地利用现有的存储过程,ROLLBACK那么就会发出声明,那么你的原因就会丢失.