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ステートメントでランタイムエラーが発生すると、トランザクション全体が終了してロールバックされます。
それは、明示的なトランザクションを使用する必要があるということですか?
これは設計によるものですTM、 Connect で見ることができるように、同様の質問に対するSQL Serverチームの応答によって:
ご意見ありがとうございます。設計上、XACT_ABORT設定オプションはRAISERRORステートメントの動作に影響を与えません。 SQL Serverの今後のリリースでこの動作を変更するためのフィードバックを検討します。
はい、これはRAISERROR
を高い重大度(16
)は、SQL実行エラーと同じです-そうではありません。
回避策は、必要なことだけであり、明示的なトランザクションを使用しても、変更する動作に影響はありません。
Try/catchブロックを使用する場合、重大度11-19のレイザーエラー番号により、実行が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
RAISERROR()
の直後にRETURN
を使用すると、それ以上プロシージャは実行されません。
[〜#〜] msdn [〜#〜] で指摘したように、THROW
の代わりにRAISERROR
ステートメントを使用する必要があります。
この2つの動作は わずかに異なる です。でもいつ XACT_ABORT
がONに設定されている場合、常にTHROW
コマンドを使用する必要があります。