it-swarm-ko.tech

xact_abort가 켜져있을 때 발생하는 오류 후에 Sql Server가 계속 실행되는 이유는 무엇입니까?

방금 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 문에서 런타임 오류가 발생하면 전체 트랜잭션이 종료되고 롤백됩니다.

이는 명시 적 거래를 사용해야한다는 의미입니까?

84
Eric Z Beard

이것은 의도적으로TM비슷한 질문에 대한 SQL Server 팀의 응답으로 Connect 에서 볼 수 있듯이

의견을 보내 주셔서 감사합니다. 의도적으로 XACT_ABORT 설정 옵션은 RAISERROR 문의 동작에 영향을 미치지 않습니다. 향후 릴리스의 SQL Server에서이 동작을 수정하기 위해 귀하의 의견을 고려할 것입니다.

예, 이것은 16와 같이 심각도가 높은 RAISERROR이 SQL 실행 오류와 동일하기를 바라는 일부 사람들에게는 약간의 문제입니다.

해결 방법은 수행해야 할 작업에 관한 것이며 명시 적 트랜잭션을 사용해도 변경하려는 동작에 영향을 미치지 않습니다.

45
Philip Rieck

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
23
ninegrid

RAISERROR() 바로 다음에 RETURN를 사용하면 프로 시저가 더 이상 실행되지 않습니다.

22
piyush

MSDN 에서 지적했듯이 THROW 문 대신 RAISERROR 문을 사용해야합니다.

둘은 약간 다르게 동작합니다. 하지만 때 XACT_ABORT가 ON으로 설정되어 있으면 항상 THROW 명령을 사용해야합니다.

13
Möoz