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 문에서 런타임 오류가 발생하면 전체 트랜잭션이 종료되고 롤백됩니다.
그것은 내가 명시 적 거래를 사용해야한다는 것을 의미합니까?
답변
이것은 유사한 질문에 대한 SQL Server 팀의 응답 으로 Connect에서 볼 수 있듯이 By Design TM입니다 .
의견을 보내 주셔서 감사합니다. 의도적으로 XACT_ABORT 설정 옵션은 RAISERROR 문의 동작에 영향을주지 않습니다. 향후 SQL Server 릴리스에서이 동작을 수정하기 위해 귀하의 의견을 고려할 것입니다.
예, 이것은 RAISERROR
높은 심각도 (예 🙂 16
가 SQL 실행 오류와 동일 할 것으로 기대 하는 일부에게는 약간의 문제입니다 . 그렇지 않습니다.
해결 방법은 수행해야 할 작업에 관한 것이며 명시 적 트랜잭션을 사용하더라도 변경하려는 동작에 영향을주지 않습니다.
답변
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
답변
RETURN
즉시 사용하면 RAISERROR()
더 이상 절차가 실행되지 않습니다.
답변
에 대한 문서에서 지적했듯이 SET XACT_ABORT
, THROW
대신 문을 사용해야합니다 RAISERROR
.
둘은 약간 다르게 동작합니다 . 그러나 XACT_ABORT
가 ON으로 설정 되면 항상 THROW
명령을 사용해야합니다 .