• SQL Server嵌套事务


    一、@@TRANCOUNT

    在将事务前,我们先来了解一下@@TRANCOUNT ,@@trancount返回上传执行begin transaction语句的事务计数。

    1、每执行一次begin transaction语句@@trancount 将增加1。

    2、执行rollback transaction 语句@@trancount将变为0,但执行rollback transaction savepoint_name语句@@trancount不会有影响。

    declare @currntTranCount VARCHAR
    set @currntTranCount=@@TRANCOUNT
    print '未执行事务前全局@@TRANCOUNT:'+@currntTranCount
    
    begin transaction
    set @currntTranCount=@@TRANCOUNT
    print '执行事务后全局@@TRANCOUNT:'+@currntTranCount
    
    rollback transaction
    set @currntTranCount=@@TRANCOUNT
    print '回滚事务后全局@@TRANCOUNT:'+@currntTranCount

    输出:

      未执行事务前全局@@TRANCOUNT:0
      执行事务后全局@@TRANCOUNT:1
      回滚事务后全局@@TRANCOUNT:0

    declare @currntTranCount VARCHAR
    set @currntTranCount=@@TRANCOUNT
    print '未执行事务前全局@@TRANCOUNT:'+@currntTranCount
    
    begin transaction
    set @currntTranCount=@@TRANCOUNT
    print '执行事务后全局@@TRANCOUNT:'+@currntTranCount
    
    save transaction savePoint_Tran1
    set @currntTranCount=@@TRANCOUNT
    print '保存事务点后全局@@TRANCOUNT:'+@currntTranCount
    
    rollback transaction savePoint_Tran1    --@@TRANCOUNT没受影响
    set @currntTranCount=@@TRANCOUNT
    print '回滚事务保存点后全局@@TRANCOUNT:'+@currntTranCount
    
    rollback transaction
    set @currntTranCount=@@TRANCOUNT
    print '回滚事务后全局@@TRANCOUNT:'+@currntTranCount

    输出:

      未执行事务前全局@@TRANCOUNT:0
      执行事务后全局@@TRANCOUNT:1
      保存事务点后全局@@TRANCOUNT:1
      回滚事务保存点后全局@@TRANCOUNT:1
      回滚事务后全局@@TRANCOUNT:0

    3、执行commit transaction或commit work语句@@trancount将递减1。

    declare @currntTranCount VARCHAR
    set @currntTranCount=@@TRANCOUNT
    print '未执行事务前全局@@TRANCOUNT:'+@currntTranCount
    
    begin transaction
    set @currntTranCount=@@TRANCOUNT
    print '执行事务后全局@@TRANCOUNT:'+@currntTranCount
    
    commit transaction
    set @currntTranCount=@@TRANCOUNT
    print '提交事务后全局@@TRANCOUNT:'+@currntTranCount

    输出:

      未执行事务前全局@@TRANCOUNT:0
      执行事务后全局@@TRANCOUNT:1
      提交事务后全局@@TRANCOUNT:0

    4、ROLLBACK TRANSACTION (Transact-SQL):https://msdn.microsoft.com/zh-cn/library/ms181299.aspx

    5、COMMIT TRANSACTION (Transact-SQL):https://msdn.microsoft.com/zh-cn/library/ms190295.aspx

    二、嵌套事务

    1、回滚嵌套事务:rollback transaction只能回滚最外面的事务名称或rollback transaction不指定某个事务名进行回滚;

    如果执行rollback transaction transaction_innerTran,SQL会提示“无法回滚 transaction_innerTran。找不到该名称的事务或保存点。”,原因是transaction_innerTran不是最外层的事务;也就是说:回滚事务一回滚就所有事务都被回滚。

    rollback transaction可以回滚某个事务保存点(SAVE TRANSACTION savePoint_Tran), 如ROLLBACK TRAN savePoint_Tran,但是回滚事务保存点不会使事务计数@@TRANCOUNT减少。

    1)内层回滚事务存储过程

    --嵌套事务:内层事务
    CREATE PROCEDURE pro_InnerTransactionTest 
    AS
    BEGIN
        declare @currntTranCount VARCHAR(50)
        set @currntTranCount=@@TRANCOUNT
        print '未执行内层事务前全局@@TRANCOUNT:'+@currntTranCount
        
        begin transaction transaction_innerTran
        set @currntTranCount=@@TRANCOUNT
        print '执行内层事务后全局@@TRANCOUNT:'+@currntTranCount
        
        rollback transaction transaction_innerTran
        --rollback transaction
        --rollback transaction transaction_outerTran    --rollback都报错
        set @currntTranCount=@@TRANCOUNT
        print '回滚内层事务后全局@@TRANCOUNT:'+@currntTranCount
        
        return 0;
        --return 1;
    END
    GO

    2)外层开启事务执行

    --嵌套事务:外层执行
    declare @currntTranCount varchar(50);
    declare @result int;
    set @currntTranCount=@@TRANCOUNT;
    print '未执行外层事务前全局@@TRANCOUNT:'+@currntTranCount;
        
    begin transaction transaction_outerTran;
    set @currntTranCount=@@TRANCOUNT;
    print '执行外层事务后全局@@TRANCOUNT:'+@currntTranCount;
    
    execute @result = pro_InnerTransactionTest;
    
    if(@result <= 0) 
    begin
        rollback transaction transaction_outerTran;
        set @currntTranCount=@@TRANCOUNT;
        print '回滚外层事务后全局@@TRANCOUNT:'+@currntTranCount;
                    
        return;
    end 
    commit transaction transaction_outerTran 
    set @currntTranCount=@@TRANCOUNT;
    print '提交外层事务后全局@@TRANCOUNT:'+@currntTranCount;

    输出:

    未执行外层事务前全局@@TRANCOUNT:0
    执行外层事务后全局@@TRANCOUNT:1
    未执行内层事务前全局@@TRANCOUNT:1
    执行内层事务后全局@@TRANCOUNT:2
    消息 6401,级别 16,状态 1,过程 pro_InnerTransactionTest,第 13 行
    无法回滚 transaction_innerTran。找不到该名称的事务或保存点。
    回滚内层事务后全局@@TRANCOUNT:2
    消息 266,级别 16,状态 2,过程 pro_InnerTransactionTest,第 0 行
    EXECUTE 后的事务计数指示 BEGIN 和 COMMIT 语句的数目不匹配。上一计数 = 1,当前计数 = 2。
    回滚外层事务后全局@@TRANCOUNT:0

    2、提交嵌套事务:commit transaction 可以单独指定某个事务名,如transaction_outerTran,transaction_innerTran进行提交,但即使transaction_innerTran提交成功了,只要最外面的事务transaction_outerTran回滚,transaction_innerTran提交的数据也会被回滚的。

    1)内层提交事务存储过程

    --嵌套事务:内层事务
    CREATE PROCEDURE pro_InnerTransactionTest 
    AS
    BEGIN
        declare @currntTranCount VARCHAR(50)
        set @currntTranCount=@@TRANCOUNT
        print '未执行内层事务前全局@@TRANCOUNT:'+@currntTranCount
        
        begin transaction transaction_innerTran
        set @currntTranCount=@@TRANCOUNT
        print '执行内层事务后全局@@TRANCOUNT:'+@currntTranCount
        
        --rollback transaction transaction_innerTran
        --rollback transaction
        --rollback transaction transaction_outerTran    --rollback都报错
        --set @currntTranCount=@@TRANCOUNT
        --print '回滚内层事务后全局@@TRANCOUNT:'+@currntTranCount
        
        commit transaction transaction_innerTran    --提交内层事务,外层回滚或提交事务都没报错
        
        return 0;
        --return 1;
    END
    GO

    2)外层开启事务执行后输出:

    未执行外层事务前全局@@TRANCOUNT:0
    执行外层事务后全局@@TRANCOUNT:1
    未执行内层事务前全局@@TRANCOUNT:1
    执行内层事务后全局@@TRANCOUNT:2
    回滚外层事务后全局@@TRANCOUNT:0  (或:提交外层事务后全局@@TRANCOUNT:0)

    三、解决办法:

    1)内层存储过程

    --嵌套事务:内层事务
    CREATE PROCEDURE pro_InnerTransactionTest 
    AS
    BEGIN
        declare @currntTranCount varchar(50)
        declare @sumError int=0
        declare @isSingleTran bit=1    --是否单个事务而非嵌套事务
        
        set @currntTranCount=@@TRANCOUNT
        print '未执行内层事务前全局@@TRANCOUNT:'+@currntTranCount
        
        SET XACT_ABORT ON    --设置事务回滚到原点
        --开始事务
        if (@currntTranCount=0)
         begin
            begin transaction transaction_innerTran
            set @isSingleTran=1;        
            set @currntTranCount=@@TRANCOUNT
            print '执行内层事务后全局@@TRANCOUNT:'+@currntTranCount
         end
        else
         begin
            save transaction savepoint_innerTran    --保存事务点
            set @isSingleTran=0;        
            set @currntTranCount=@@TRANCOUNT
            print '保存内层事务点全局@@TRANCOUNT:'+@currntTranCount
         end
        
        --UPDATE [dbo].[FinanceInfo] SET [Balance] = [Balance]+1000 WHERE [UserId] = 10001
        --set @sumError = @sumError + @@error
        --UPDATE [dbo].[FinanceInfo] SET [Balance] = [Balance]-1000 WHERE [UserId] = 10002
        --set @sumError = @sumError + @@error
        
        set @sumError=1; --手动测试
        
        if(@sumError = 0)
         begin
            if(@isSingleTran = 1) 
             begin
                commit transaction transaction_innerTran
                set @currntTranCount=@@TRANCOUNT
                print '提交内层事务点全局@@TRANCOUNT:'+@currntTranCount
             end
            else
             begin
                set @currntTranCount=@@TRANCOUNT
                print '返回内层全局@@TRANCOUNT:'+@currntTranCount
                return 1;            --成功
             end        
         end
        else
         begin
            if(@isSingleTran = 1) 
             begin
                rollback transaction    --发生错误,回滚事务
                set @currntTranCount=@@TRANCOUNT
                print '回滚内层事务全局@@TRANCOUNT:'+@currntTranCount
             end
            else
             begin
                rollback transaction savepoint_innerTran
                set @currntTranCount=@@TRANCOUNT
                print '回滚内层事务点全局@@TRANCOUNT:'+@currntTranCount
                return 0;                --失败
             end
         end
    END
    GO

    2)外层开启事务执行存储过程

    --嵌套事务:外层执行
    declare @currntTranCount varchar(50);
    declare @result int;
    set @currntTranCount=@@TRANCOUNT;
    print '未执行外层事务前全局@@TRANCOUNT:'+@currntTranCount;
        
    begin transaction transaction_outerTran;
    set @currntTranCount=@@TRANCOUNT;
    print '执行外层事务后全局@@TRANCOUNT:'+@currntTranCount;
    
    execute @result = pro_InnerTransactionTest;
    
    if(@result <= 0) 
    begin
        rollback transaction transaction_outerTran;
        set @currntTranCount=@@TRANCOUNT;
        print '回滚外层事务后全局@@TRANCOUNT:'+@currntTranCount;
                    
        return;
    end 
    commit transaction transaction_outerTran 
    set @currntTranCount=@@TRANCOUNT;
    print '提交外层事务后全局@@TRANCOUNT:'+@currntTranCount;
    
    
    --单个事务执行
    declare @result INT
    execute @result = pro_InnerTransactionTest;
    select @result
  • 相关阅读:
    spring六种种依赖注入方式
    HibernateDaoSupport 源码
    ServiceStack.Redis常用操作
    ServiceStack.Redis 之 IRedisTypedClient
    ServiceStack.Redis之IRedisClient
    Windows下Redis的安装使用
    为什么要使用SLF4J而不是Log4J
    每天一个linux命令:mkdir
    浅谈Redis及其安装配置
    Solr4.4的安装与配置
  • 原文地址:https://www.cnblogs.com/lusunqing/p/4340865.html
Copyright © 2020-2023  润新知