2

我有一个更大的存储过程,它利用几个 TRY/CATCH 块来捕获和记录单个错误。我还围绕程序的全部内容包装了一个事务,以便能够在沿途某处出现错误的情况下回滚整个事情(以防止大量混乱的清理);XACT_ABORT 已启用,否则它不会回滚整个事务。

关键组件:
我的数据库中有一个表,每次运行此过程时都会插入一条记录,其中包含操作结果和问题的详细信息。

有趣的事情正在发生 - 实际上,当我终于弄清楚出了什么问题时,这很明显......我的日志表中的插入语句也正在回滚,因此,如果我没有在 SSMS 中运行它,我什至看不到这是否已运行,因为回滚会删除所有活动的恍惚状态。

问题:
除了这个单一的插入语句之外,是否有可能让整个事务回滚?我仍然想保留在存储过程运行期间编译的错误消息。

非常感谢!

~以利

更新 6/28
这是我正在查看的代码示例。这与@Alex 和@gameiswar 提出的样本之间的主要区别在于,在我的例子中,try/catch 块都嵌套在单个事务中。这样做的目的是有多个捕获(对于多个表),尽管即使最后一次更新失败,我们也会回滚整个混乱。

SET XACT_ABORT ON;  
BEGIN TRANSACTION  
    DECLARE @message AS VARCHAR(MAX) = '';  

    -- TABLE 1
    BEGIN TRY
        UPDATE TABLE xx 
        SET yy = zz
    END TRY
    BEGIN CATCH
        SET @message = 'TABLE 1 '+ ERROR_MESSAGE();

        INSERT INTO LOGTABLE
        SELECT 
            GETDATE(),
            @message
        RETURN;
    END CATCH

    -- TABLE 2
    BEGIN TRY
        UPDATE TABLE sss 
        SET tt = xyz
    END TRY
    BEGIN CATCH
        SET @message = 'TABLE 2 '+ ERROR_MESSAGE();

        INSERT INTO LOGTABLE
        SELECT 
            GETDATE(),
            @message
        RETURN;
    END CATCH
COMMIT TRANSACTION
4

3 回答 3

2

您可以尝试以下类似的方法,以确保您记录操作。这利用了表变量不会回滚的事实。

伪代码只是为了给你一个想法:

create table test1
(
id int primary key
)

create table logg
(
errmsg varchar(max)
)



declare @errmsg varchar(max)

set xact_abort on
begin try
begin tran
insert into test1
select 1

insert into test1
select 1

commit
end try

begin catch
set @errmsg=ERROR_MESSAGE()
select @errmsg as "in block"
if @@trancount>0
rollback tran

end catch
set xact_abort off


select @errmsg as "after block";

insert into logg
select @errmsg


select * from logg
于 2016-06-28T03:20:27.297 回答
1

我不知道细节,但恕我直言,一般逻辑可以是这样的。

--set XACT_ABORT ON --not include it
declare @result varchar(max) --collect details in case you need it
begin transaction
begin try
--your logic here
--if something wrong RAISERROR(...@result)
--everything OK
commit
end try
begin catch
--collect error_message() and other into @result
rollback
end catch
insert log(result) values (@result)
于 2016-06-27T21:16:59.847 回答
1

好的...我能够结合使用 Alex 和 GameisWar 提出的伟大建议以及添加 T-SQL GOTO 控制流语句来解决这个问题。

基本思想是将错误消息存储在一个变量中,该变量在回滚后仍然存在,然后让 Catch 将您发送到一个 FAILURE 标签,该标签将执行以下操作:

  • 回滚事务
  • 使用上述变量中​​的数据将记录插入日志表
  • 退出存储过程

我还使用第二个 GOTO 语句来确保成功运行将跳过 FAILURE 部分并提交事务。

下面是测试 SQL 的代码片段。它就像一个魅力,我已经在我们的生产环境中实现了它并(成功地)测试了它。

我非常感谢所有的帮助和投入!

SET XACT_ABORT ON               
DECLARE @MESSAGE VARCHAR(MAX) = '';

BEGIN TRANSACTION 
    BEGIN TRY
        INSERT INTO TEST_TABLE VALUES ('TEST');     -- WORKS FINE
    END TRY 
    BEGIN CATCH     
        SET @MESSAGE = 'ERROR - SECTION 1: ' + ERROR_MESSAGE();
        GOTO FAILURE;
    END CATCH

    BEGIN TRY
        INSERT INTO TEST_TABLE VALUES ('TEST2');        --WORKS FINE
        INSERT INTO TEST_TABLE VALUES ('ANOTHER TEST'); -- ERRORS OUT, DATA WOULD BE TRUNCATED
    END TRY 
    BEGIN CATCH 
        SET @MESSAGE = 'ERROR - SECTION 2: ' + ERROR_MESSAGE();
        GOTO FAILURE;
    END CATCH

GOTO SUCCESS;

FAILURE:        
    ROLLBACK
    INSERT INTO LOGG SELECT @MESSAGE
    RETURN; 

SUCCESS:
COMMIT TRANSACTION
于 2016-07-06T21:33:43.410 回答