SQL Server作业中的事务和try-catch


9

在SQL Server作业的每个步骤中,我们都有DML操作。为了确保更新/插入的情况下将回滚不顺心的事,我已经包裹的数据修改的每一步TRY CATCHTRANSACTION块:

BEGIN TRY
    BEGIN TRANSACTION

        [[INSERT/update statements]] ...

    IF @@TRANCOUNT > 0
    BEGIN
        COMMIT TRANSACTION
        PRINT 'Successful.'
    END

END TRY

BEGIN CATCH
    SELECT
        ERROR_NUMBER() AS ErrorNumber,
        ERROR_SEVERITY() AS ErrorSeverity,
        ERROR_STATE() AS ErrorState,
        ERROR_PROCEDURE() AS ErrorProcedure,
        ERROR_LINE() AS ErrorLine,
        ERROR_MESSAGE() AS ErrorMessage

    IF @@TRANCOUNT > 0
    BEGIN
        ROLLBACK TRANSACTION
        PRINT 'Unsuccessful.'
    END
END CATCH

是否确保在发生错误的情况下回滚数据操作?还是应该考虑其他因素?

会有更好的方法(使用配置等)吗?

谢谢。

Answers:


7

我宁愿推荐一种类似Exception Handling and Nested Transactions的模式

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

该模式检查XACT_STATE()catch块中的,以防止不可提交的事务

不可提交的事务和XACT_STATE
如果在TRY块中生成的错误导致当前事务的状态无效,则该事务被归类为不可提交的事务。通常,在TRY块外部结束事务的错误会导致事务在TRY块内部发生错误时进入不可提交状态。不可提交的事务只能执行读取操作或ROLLBACK TRANSACTION。事务不能执行任何会生成写操作或COMMIT TRANSACTION的Transact-SQL语句。如果一个事务已被分类为不可提交的事务,则XACT_STATE函数返回值-1。批处理完成后,数据库引擎将回滚所有活动的不可提交的事务。如果在事务进入不可提交状态时未发送任何错误消息,批处理完成后,错误消息将发送到客户端应用程序。这表明检测到不可提交的事务并将其回滚。

您的代码正在检查@@TRANCOUNT不能为0的位置,它使用了信息性PRINT消息和SELECT结果集的混合来表示成功,它不处理可恢复的错误。理想情况下,异常应传播到客户端,在这种情况下应传播到代理作业(即,应重新引发渔获量)。


感谢您的有用回答和出色的网站!但是我想知道是否仍然可以将这种模式用于简单的DML语句(而不是存储的proc)?另外,我们是否还必须保存以下交易?(我没有要使用的存储过程):保存事务usp_my_procedure_name;
2013年

2

你的东西对我来说看起来不错。我建议您在回滚事务后,当然要对信息做些事,例如将其写到日志中。


1
感谢您的答复,能否请您提示我如何将其写入日志?
2013年

3
如果要将错误或数据写入日志表,则在执行回滚之前,请将所需的数据复制到表变量(使用表变量很重要,临时表将被回滚。)然后执行回滚,然后将数据从表变量插入到日志表中。
HLGEM
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.