【问题标题】:SQL Server Rollback Transaction From Error Outside TransactionSQL Server 从事务外部错误回滚事务
【发布时间】:2018-10-09 10:03:03
【问题描述】:

我有一个 TRY CATCH 语句,其中 TRY 中的事务中有一些代码,而 TRY 中有事务之外的其他代码。如果事务外部的代码(仍在 TRY 中)抛出错误,我的事务不会在 CATCH 中回滚。但是,如果我删除事务之外的代码,如果出现错误,事务将被回滚。为什么会这样? SQL Server 2014。

我的代码。如果 Remote proc 抛出错误,则事务不会回滚:

BEGIN  try
begin transaction

  INSERT INTO bos_south_florida_job_map (job_id, original_job_id, 
created_date, updated_date,completed_status_sent_ind, 
assigned_status_sent_ind, status_prev)
VALUES (9999, '1234', getdate(), getdate(),0,0,'CREATED');


COMMIT TRANSACTION

declare @sql varchar(max)

set @sql = ''

select @sql = '
   declare @error1 varchar(255),
   @error2 varchar(255),
   @error3 varchar(255)

   Exec NEXTGEN.DBO.wbAf_ConfirmDispatchedReservation ''AFFWEB'', ''A10596'', ''Admin'', ''Admin'', '''+cast(preassignedsubconcode as varchar(100))+''', '''+cast('1234' as varchar(20))+''', '''+convert(char(23),ISNULL(ScheduledDispatchDateTime,''),121)+''', '+cast('1234' as varchar(12))+',null,@error1 output,@error2 output,@error3 output

   if @error1 is not null or @error2 is not null or @error3 is not null
   begin
        set @error1 = @error2 + '' '' + @error3 + '' '' + @error1
        RAISERROR (@error1, 16, 1)
   end
   '
from [BCCUATWSQL290].NEXTGEN.DBO.tbRideResCurDispatch
where resno = '35002616'

   exec(@sql) at [BCCUATWSQL290]

END TRY

BEGIN CATCH

IF @@TRANCOUNT > 0  
ROLLBACK TRANSACTION

DECLARE @ErrorMessage NVARCHAR(4000);
DECLARE @ErrorSeverity INT;
DECLARE @ErrorState INT;

SELECT
@ErrorMessage = ERROR_MESSAGE(),
@ErrorSeverity = ERROR_SEVERITY(),
@ErrorState = ERROR_STATE()

INSERT INTO bos_south_florida_error_log (original_job_id, [action], 
error_datetime, [error_message])
SELECT
    '1234',
    'CREATE_JOB',
    GETDATE(),
    'Msg: ' + ISNULL(CONVERT(VARCHAR, ERROR_NUMBER()), 'N/A') + ', Level: ' + ISNULL(CONVERT(VARCHAR, @ErrorSeverity), 'N/A') + ', Line: ' + ISNULL(CONVERT(VARCHAR, ERROR_LINE()), 'N/A') + ', Error: ' + ISNULL(@ErrorMessage, 'N/A')


END CATCH

这将回滚事务:

BEGIN  try
begin transaction

  INSERT INTO bos_south_florida_job_map (job_id, original_job_id, 
created_date, updated_date,completed_status_sent_ind, 
assigned_status_sent_ind, status_prev)
VALUES (9999, '1234', getdate(), getdate(),0,0,'CREATED');

RAISERROR ('BLAH', 16, 1)

COMMIT TRANSACTION

END TRY

BEGIN CATCH

IF @@TRANCOUNT > 0  
ROLLBACK TRANSACTION

DECLARE @ErrorMessage NVARCHAR(4000);
DECLARE @ErrorSeverity INT;
DECLARE @ErrorState INT;

SELECT
@ErrorMessage = ERROR_MESSAGE(),
@ErrorSeverity = ERROR_SEVERITY(),
@ErrorState = ERROR_STATE()

INSERT INTO bos_south_florida_error_log (original_job_id, [action], 
error_datetime, [error_message])
SELECT
    '1234',
    'CREATE_JOB',
    GETDATE(),
    'Msg: ' + ISNULL(CONVERT(VARCHAR, ERROR_NUMBER()), 'N/A') + ', Level: ' + ISNULL(CONVERT(VARCHAR, @ErrorSeverity), 'N/A') + ', Line: ' + ISNULL(CONVERT(VARCHAR, ERROR_LINE()), 'N/A') + ', Error: ' + ISNULL(@ErrorMessage, 'N/A')


END CATCH

【问题讨论】:

    标签: sql sql-server sql-server-2012


    【解决方案1】:

    回想一下,每Begin Transaction 使@@Trancount 增加 1,每Commit 减少 1。

    在您的第一个示例中,事务已经提交并且在您抛出错误之前@@TranCount 减少为零,因此在Catch 子句中,@@Trancount 将为零。这很好。已提交的事务已成为历史,无法回滚。

    在第二个示例中,当您在 Begin 之后但在 Commit 之前引发错误时,当您到达 Catch 子句时,@@TranCount 为 1,然后您回滚。

    大概你想要的行为是远程调用中的错误应该导致回滚?您可以通过将Commit 移动到远程调用之后来实现,使其成为End Try 之前的最后一条语句。

    但是请注意,跨服务器事务的成本相对较高,并且需要在两台服务器上都运行 MS DTC。 DBA 可能不喜欢在负载很重的服务器上这样做。

    顺便说一句,围绕单个插入的事务通常是没有意义的。正常行为是“自动提交”模式,这意味着插入/更新/删除语句是'bounded by an unseen BEGIN TRANSACTION and an unseen COMMIT TRANSACTION statement'

    【讨论】:

    • 感谢您的回答,有道理。跨服务器事务正是我试图避免的,但看起来这是我正在寻找的行为的唯一选择。
    • 别等了,还有其他选择!您可以运行外部服务器 exec first 并且仅在外部服务器调用成功时才运行本地插入?
    • 我需要它按这个顺序运行,就像在翻转场景中,如果插入失败,我不希望远程 proc 被执行。我认为我唯一的其他选择是“手动回滚”远程过程,并通过更新/删除来扭转它所做的更改。
    • 实际上,我确实找到了解决方法。如果我将链接服务器的 RPC 的分布式事务的启用提升属性设置为 false,我可以将远程调用放在 COMMIT TRAN 之前。
    猜你喜欢
    • 2010-12-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多