programing

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

subpage 2023. 6. 3. 08:28
반응형

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

TSQL에 있는 것 때문에 깜짝 놀랐습니다.만약 xact_abort가 켜져 있다면, 다음과 같은 것을 부르는 것이라고 생각했습니다.

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

저장 프로시저(또는 모든 배치)의 실행을 중지합니다.

하지만 제 ADO.NET 오류 메시지는 그 반대였습니다.예외 메시지에 발생한 raise 오류 메시지와 그 다음에 발생한 오류 메시지가 모두 표시되었습니다.

이것은 저의 해결 방법입니다(어쨌든 제 습관입니다). 하지만 그럴 필요는 없을 것 같습니다.

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

의사들은 다음과 같이 말합니다.

SET XACT_ABORT가 ON일 때 Transact-SQL 문이 런타임 오류를 발생시키면 전체 트랜잭션이 종료되고 롤백됩니다.

제가 명시적인 거래를 사용해야 한다는 뜻인가요?

이는 유사한 질문에 대한 SQL Server 팀의 응답별 연결에서 볼 수 있듯이 설계 기준입니다TM.

당신의 피드백에 감사드립니다.XACT_ABORT set 옵션은 설계상 RAISERROR 문의 동작에 영향을 주지 않습니다.향후 SQL Server 릴리스를 위해 이 동작을 수정하기 위해 귀하의 의견을 고려해 보겠습니다.

네, 이것은 어떤 사람들에게는 약간의 문제입니다.RAISERROR높은 심각도로(와 같은)16)는 SQL 실행 오류와 동일하지만 그렇지 않습니다.

해결 방법은 수행해야 하는 작업에 불과하며, 명시적인 트랜잭션을 사용해도 변경하려는 동작에 영향을 주지 않습니다.

시도/캐치 블록을 사용하는 경우 심각도가 11-19인 상승 오류 번호로 인해 실행이 캐치 블록으로 점프합니다.

16을 초과하는 심각도는 시스템 오류입니다.다음 코드를 시연하려면 시도/캐치 블록을 설정하고 실패할 것으로 예상되는 저장 프로시저를 실행합니다.

자리가 있다고 가정합니다.[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

사용하다RETURN직후에RAISERROR()더 이상 절차를 수행하지 않을 것입니다.

문서에 지적된 바와 같이,THROW대신 문을 사용해야 합니다.RAISERROR.

그 둘은 약간 다르게 행동합니다.하지만 언제XACT_ABORTON으로 설정된 경우 항상 다음을 사용해야 합니다.THROW지휘권

마이크로소프트는 에러를 올리는 대신 던지기를 사용할 것을 제안합니다.XACT_State를 사용하여 시도 캐치 블록에 대한 커밋 또는 롤백 결정

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

언급URL : https://stackoverflow.com/questions/76346/why-does-sql-server-keep-executing-after-raiserror-when-xact-abort-is-on

반응형