と同様の問題が発生しています。現在のトランザクションはコミットできず、ログファイルに書き込む操作をサポートできません 、しかし、フォローアップの質問があります。
そこの答えは Transact-SQLでのTRY ... CATCHの使用 で、これについては後で説明します...
私のコード(もちろん継承)は単純化された形式です:
_SET NOCOUNT ON
SET XACT_ABORT ON
CREATE TABLE #tmp
SET @transaction = 'insert_backtest_results'
BEGIN TRANSACTION @transaction
BEGIN TRY
--do some bulk insert stuff into #tmp
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION @transaction
SET @errorMessage = 'bulk insert error importing results for backtest '
+ CAST(@backtest_id as VARCHAR) +
'; check backtestfiles$ directory for error files ' +
' error_number: ' + CAST(ERROR_NUMBER() AS VARCHAR) +
' error_message: ' + CAST(ERROR_MESSAGE() AS VARCHAR(200)) +
' error_severity: ' + CAST(ERROR_SEVERITY() AS VARCHAR) +
' error_state ' + CAST(ERROR_STATE() AS VARCHAR) +
' error_line: ' + CAST(ERROR_LINE() AS VARCHAR)
RAISERROR(@errorMessage, 16, 1)
RETURN -666
END CATCH
BEGIN TRY
EXEC usp_other_stuff_1 @whatever
EXEC usp_other_stuff_2 @whatever
-- a LOT of "normal" logic here... inserts, updates, etc...
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION @transaction
SET @errorMessage = 'error importing results for backtest '
+ CAST(@backtest_id as VARCHAR) +
' error_number: ' + CAST(ERROR_NUMBER() AS VARCHAR) +
' error_message: ' + CAST(ERROR_MESSAGE() AS VARCHAR(200)) +
' error_severity: ' + CAST(ERROR_SEVERITY() AS VARCHAR) +
' error_state ' + CAST(ERROR_STATE() AS VARCHAR) +
' error_line: ' + CAST(ERROR_LINE() AS VARCHAR)
RAISERROR(@errorMessage, 16, 1)
RETURN -777
END CATCH
RETURN 0
_
私はそれで遊んでそれを自分で理解するのに十分な情報を持っていると思います...残念ながらエラーを再現することはほとんど不可能であることが証明されています。ですから、ここで質問することで、問題と解決策についての私の理解が明確になることを期待しています。
このストアドプロシージャは断続的に、次のようなエラーをスローします。
バックテスト9649の結果のインポートエラーerror_number:3930 error_message:現在のトランザクションはコミットできず、ログファイルに書き込む操作をサポートできません。トランザクションをロールバックします。 error_severity:16 error_state 1 error_line:217
したがって、明らかにエラーは2番目のcatchブロックから来ています
私が読んだことに基づいて Transact-SQLでTRY ... CATCHを使用する 、私は何が起こっていると思う例外がスローされたとき、 _XACT_ABORT
_ を使用すると、トランザクションが「終了してロールバック」されます...その後、_BEGIN CATCH
_の最初の行が盲目的に再度ロールバックを試みます。
元の開発者が_XACT_ABORT
_を有効にした理由がわからないので、(削除するよりも)より良い解決策は、トランザクションがある場合にのみロールバックするXACT_STATE()
を使用することだと思います( _<>0
_)。それは理にかなっていますか?何か不足していますか?
また、エラーメッセージにログインすることについて言及すると、疑問が生じます。構成に潜在的に別の問題がありますか?このシナリオでのRAISEERROR()
の使用は問題の原因ですか?エラーメッセージが示すように、ロギングが不可能な何らかの種類の場合、それはログに記録されますか?
XACT_STATE()
を常に確認する必要があります。XACT_ABORT
設定。 例外処理とネストされたトランザクション でTRY/CATCHコンテキストのトランザクションを処理する必要があるストアドプロシージャのテンプレートの例を持っています。
create procedure [usp_my_procedure_name]
as
begin
set nocount on;
declare @trancount int;
set @trancount = @@trancount;
begin try
if @trancount = 0
begin transaction
else
save transaction usp_my_procedure_name;
-- Do the actual work here
lbexit:
if @trancount = 0
commit;
end try
begin catch
declare @error int, @message varchar(4000), @xstate int;
select @error = ERROR_NUMBER(),
@message = ERROR_MESSAGE(),
@xstate = XACT_STATE();
if @xstate = -1
rollback;
if @xstate = 1 and @trancount = 0
rollback
if @xstate = 1 and @trancount > 0
rollback transaction usp_my_procedure_name;
raiserror ('usp_my_procedure_name: %d: %s', 16, 1, @error, @message) ;
end catch
end
上記の議論にはいくつかの誤解があります。
まず、トランザクションの状態に関係なく、いつでもトランザクションをロールバックできます。したがって、ロールバックの前ではなく、COMMITの前にXACT_STATEを確認するだけで済みます。
コードのエラーに関する限り、トランザクションをTRY内に配置する必要があります。次に、CATCHで最初に行うべきことは次のとおりです。
IF @@TRANCOUNT > 0
ROLLBACK TRANSACTION @transaction
次に、上記のステートメントの後、メールまたは必要なものを送信できます。 (参考:ロールバックの前にメールを送信すると、「ログファイルに書き込むことができません...」エラーが必ず表示されます。)
この問題は昨年のものでしたので、今までにこれを解決していただければ幸いです。
経験則として...エラーが発生すると、TRYはすぐにCATCHにジャンプします。次に、CATCHにいるときに、XACT_STATEを使用してコミットできるかどうかを決定できます。ただし、キャッチで常にロールバックする場合は、状態をチェックする必要はまったくありません。