xact_abort가 켜져있을 때 SQL Server가 raiserror 후 계속 실행되는 이유는 무엇입니까?


87

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

그것은 내가 명시 적 거래를 사용해야한다는 것을 의미합니까?


방금 테스트 RAISERROR했으며 심각도가 16이 아닌 17 또는 18로 설정된 경우 실제로 실행을 종료합니다.
개정

2
방금 SQL Server 2012를 테스트 했으며 심각도가 16이 아닌 17 또는 18로 설정된 경우 RAISERROR실제로 실행을 종료 하지 않습니다.
Ian Boyd

1
그 이유는 Erland Sommarskog (2001 년 이후 SQL Server MVP) 의 뛰어난 SQL Server의 오류 및 트랜잭션 처리 시리즈 2 부 에서 명확하게 설명합니다 . "가끔 SQL Server가 의도적으로 다음과 같이 설계되었다는 느낌을받습니다. 가능한 한 혼란 스럽습니다. 새 릴리스를 계획 할 때 사용자를 혼란스럽게하기 위해 이번에는 무엇을 할 수 있는지 서로에게 묻습니다 . 때로는 아이디어가 약간 떨어지지 만 누군가가 오류 처리로 무언가를 하자고 말합니다. "
Reversed Engineer

@IanBoyd, 실제로 심각도를 17 또는 18 또는 19로 설정 한 후에도 실행이 중지 되지 않습니다 . 더 흥미로운 것은 Messages탭을 보면 아니오 (X rows affected)또는 PRINT메시지 가 표시됩니다. 내가 말하고 싶은 것은 완전한 거짓말입니다 !
Gabrielius

답변:


48

이것은 유사한 질문에 대한 SQL Server 팀의 응답 으로 Connect에서 볼 수 있듯이 By Design TM입니다 .

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

예, 이것은 RAISERROR높은 심각도 (예 :) 16가 SQL 실행 오류와 동일 할 것으로 기대 하는 일부에게는 약간의 문제입니다 . 그렇지 않습니다.

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


1
고마워 필립. 참조하신 링크를 사용할 수없는 것 같습니다.
Eric Z Beard

2
링크가 정상적으로 작동합니다. 검색해야하는 경우 제목 "Have RAISERROR work with XACT_ABORT", 작성자 "jorundur", ID : 275308
JohnC

링크가 작동 하지 않고 archive.org 캐시 사본없습니다. 그것은 시간의 모래에 영원히 잃어 버렸습니다.
Ian Boyd

이 답변 은 좋은 백업 입니다.이 동작이 명확하게 설명 된 문서에 대한 링크가 있습니다.
pcdev

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

22

RETURN즉시 사용하면 RAISERROR()더 이상 절차가 실행되지 않습니다.


8
전화 rollback transaction하기 전에 전화 를 걸 수 있습니다 return.
Mike Christian

1
아마 당신은 당신의 catch 블록에서 뭔가를해야 할 수도 있습니다
sqluser

14

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

둘은 약간 다르게 동작합니다 . 그러나 XACT_ABORT가 ON으로 설정 되면 항상 THROW명령을 사용해야합니다 .


25
2k12 (또는 그 이상이 나올 때)가 없으면 가질 THROW 문이 없습니다.
Jeff Moden
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.