92

我只是对 TSQL 中的某些东西感到惊讶。我认为如果 xact_abort 开启,调用类似

raiserror('Something bad happened', 16, 1);

将停止执行存储过程(或任何批处理)。

但我的 ADO.NET 错误消息正好相反。我在异常消息中收到了 raiserror 错误消息,以及在那之后发生的下一个问题。

这是我的解决方法(无论如何这是我的习惯),但似乎没有必要:

if @somethingBadHappened
    begin;
        raiserror('Something bad happened', 16, 1);
        return;
    end;

文档是这样说的:

当 SET XACT_ABORT 为 ON 时,如果 Transact-SQL 语句引发运行时错误,则整个事务将终止并回滚。

这是否意味着我必须使用显式事务?

4

5 回答 5

49

这是 By Design TM,您可以在 SQL Server 团队对类似问题的回答中看到Connect :

感谢您的反馈意见。按照设计,XACT_ABORT 设置选项不会影响 RAISERROR 语句的行为。我们将考虑您的反馈,以便为 SQL Server 的未来版本修改此行为。

是的,对于一些希望RAISERROR高严重性(如16)与 SQL 执行错误相同的人来说,这是一个问题——事实并非如此。

您的解决方法就是您需要做的事情,并且使用显式事务对您想要更改的行为没有任何影响。

于 2008-09-16T20:12:29.123 回答
25

如果您使用 try/catch 块,严重性为 11-19 的 raiserror 错误号将导致执行跳转到 catch 块。

任何高于 16 的严重性都是系统错误。为了演示以下代码,我们设置了一个 try/catch 块并执行我们假设会失败的存储过程:

假设我们有一个表 [dbo].[Errors] 来保存错误假设我们有一个存储过程 [dbo].[AssumeThisFails],当我们执行它时会失败

-- first lets build a temporary table to hold errors
if (object_id('tempdb..#RAISERRORS') is null)
 create table #RAISERRORS (ErrorNumber int, ErrorMessage varchar(400), ErrorSeverity int, ErrorState int, ErrorLine int, ErrorProcedure varchar(128));

-- this will determine if the transaction level of the query to programatically determine if we need to begin a new transaction or create a save point to rollback to
declare @tc as int;
set @tc = @@trancount;
if (@tc = 0)
 begin transaction;
else
 save transaction myTransaction;

-- the code in the try block will be executed
begin try
 declare @return_value = '0';
 set @return_value = '0';
 declare
  @ErrorNumber as int,
  @ErrorMessage as varchar(400),
  @ErrorSeverity as int,
  @ErrorState as int,
  @ErrorLine as int,
  @ErrorProcedure as varchar(128);


 -- assume that this procedure fails...
 exec @return_value = [dbo].[AssumeThisFails]
 if (@return_value <> 0)
  raiserror('This is my error message', 17, 1);

 -- the error severity of 17 will be considered a system error execution of this query will skip the following statements and resume at the begin catch block
 if (@tc = 0)
  commit transaction;
 return(0);
end try


-- the code in the catch block will be executed on raiserror("message", 17, 1)
begin catch
  select
   @ErrorNumber = ERROR_NUMBER(),
   @ErrorMessage = ERROR_MESSAGE(),
   @ErrorSeverity = ERROR_SEVERITY(),
   @ErrorState = ERROR_STATE(),
   @ErrorLine = ERROR_LINE(),
   @ErrorProcedure = ERROR_PROCEDURE();

  insert #RAISERRORS (ErrorNumber, ErrorMessage, ErrorSeverity, ErrorState, ErrorLine, ErrorProcedure)
   values (@ErrorNumber, @ErrorMessage, @ErrorSeverity, @ErrorState, @ErrorLine, @ErrorProcedure);

  -- if i started the transaction
  if (@tc = 0)
  begin
   if (XACT_STATE() <> 0)
   begin
     select * from #RAISERRORS;
    rollback transaction;
    insert into [dbo].[Errors] (ErrorNumber, ErrorMessage, ErrorSeverity, ErrorState, ErrorLine, ErrorProcedure)
     select * from #RAISERRORS;
    insert [dbo].[Errors] (ErrorNumber, ErrorMessage, ErrorSeverity, ErrorState, ErrorLine, ErrorProcedure)
     values (@ErrorNumber, @ErrorMessage, @ErrorSeverity, @ErrorState, @ErrorLine, @ErrorProcedure);
    return(1);
   end
  end
  -- if i didn't start the transaction
  if (XACT_STATE() = 1)
  begin
   rollback transaction myTransaction;
   if (object_id('tempdb..#RAISERRORS') is not null)
    insert #RAISERRORS (ErrorNumber, ErrorMessage, ErrorSeverity, ErrorState, ErrorLine, ErrorProcedure)
     values (@ErrorNumber, @ErrorMessage, @ErrorSeverity, @ErrorState, @ErrorLine, @ErrorProcedure);
   else
    raiserror(@ErrorMessage, @ErrorSeverity, @ErrorState);
   return(2); 
  end
  else if (XACT_STATE() = -1)
  begin
   rollback transaction;
   if (object_id('tempdb..#RAISERRORS') is not null)
    insert #RAISERRORS (ErrorNumber, ErrorMessage, ErrorSeverity, ErrorState, ErrorLine, ErrorProcedure)
     values (@ErrorNumber, @ErrorMessage, @ErrorSeverity, @ErrorState, @ErrorLine, @ErrorProcedure);
   else
    raiserror(@ErrorMessage, @ErrorSeverity, @ErrorState);
   return(3);
  end
 end catch
end
于 2008-09-16T21:09:44.820 回答
24

RETURN之后立即使用RAISERROR(),它不会进一步执行该过程。

于 2011-05-13T11:16:40.017 回答
15

正如 docs 中所指出的那样,应该使用SET XACT_ABORT该语句而不是.THROWRAISERROR

两者的行为略有不同。但是当XACT_ABORT设置为 ON 时,您应该始终使用该THROW命令。

于 2013-08-14T03:18:10.100 回答
0

微软建议使用 throw 而不是 raiserror。使用 XACT_State 确定 try catch 块的提交或回滚

set XACT_ABORT ON;

BEGIN TRY
     BEGIN TRAN;
    
     insert into customers values('Mark','Davis','markdavis@mail.com', '55909090');
    insert into customer values('Zack','Roberts','zackroberts@mail.com','555919191');
    COMMIT TRAN;
  END TRY

BEGIN CATCH
    IF XACT_STATE()=-1
        ROLLBACK TRAN;
    IF XACT_STATE()=1
       COMMIT TRAN;
    SELECT ERROR_MESSAGE() AS error_message
END CATCH
于 2021-08-03T14:07:44.793 回答