トランザクションVSの開始トランザクション(SQL Server)の開始(SQL Server)トランザクションをうまくネストする方法
-
14-12-2019 - |
質問
私はセーブポイントを設定する必要があるストアドプロシージャーを持っています。そのような状況下で、それが行ったものすべてを元に戻し、呼び出し元にエラーコードを返したり、それを受け入れたり、発信者に成功を返すことができるようにする必要があります。しかし、発信者がすでにトランザクションを開始したかどうかをうまく機能させるのが必要です。この主題についてはDOCは非常に混乱しています。これは私が働くものですが、私はすべての影響の一定ではありません。
ものです - このStored Procedure (SP)
は他のものによって呼ばれます。だから私は彼らがトランザクションを開始したかどうかわからない...私がユーザーが私のSPを使用するためにトランザクションを起動する必要があるとしても、私はまだSave Points
の適切な使用に関する質問があります...
マイSPはトランザクションが進行中の場合はテストします。そうでない場合は、BEGIN TRANSACTION
で1つ起動します。トランザクションがすでに進行中の場合は、代わりにSAVE TRANSACTION MySavePointName
で保存ポイントを作成し、これが私がしたことです。
それから私が私の変更をロールバックしなければならない場合は、早くBEGIN TRANSACTION
を実行した場合は、ROLLBACK TRANSACTION
になります。節約ポイントをした場合は、ROLLBACK TRANSACTION MySavePointName
になります。このシナリオは素晴らしい仕事をしているようです。
ここに少し混乱しているところです。しかし、私がセーブポイントを作成した場合COMMIT TRANSACTION
を試しましたが、発信者はそのトランザクションをコミットしてエラーを取得しようとします。
コミットトランザクション要求には対応する開始トランザクションがありません。
だから私は疑問に思う - 保存点をロールバックすることができます(その機能:COMMIT TRANSACTION MySavePointName
は発信者のトランザクションをロールバックしません)。しかし、おそらくそれを「コミット」する必要はありませんか?それはただそこに留まります、あなたがそれにロールバックする必要があるが、元の取引がコミットされたら(またはロールバックされた)かどうかを遅らないでください。
取引を「巣」する方法がある場合は、いくつかの光を当ててください。 ROLLBACK TRANSACTION MySavePointName
でネストする方法を考え出していませんが、LOLLBACKまたは自分の内部取引をコミットするだけです。 BEGIN TRANSACTION
は常にトップトランザクションにロールバックされますが、ROLLBACK
は単にCOMMIT
を減らすだけです。
解決
私はこれを今すべて出したと信じているので、私は自分の質問に答えます...
だから私のSPはこのようなもので始まり、何もない場合は新しいトランザクションを開始しますが、保存点を使用している場合は保存点を使用してください。
DECLARE @startingTranCount int
SET @startingTranCount = @@TRANCOUNT
IF @startingTranCount > 0
SAVE TRANSACTION mySavePointName
ELSE
BEGIN TRANSACTION
-- …
.
その後、変更をコミットする準備ができたら、自分自身の取引を開始した場合にのみコミットする必要があります。
IF @startingTranCount = 0
COMMIT TRANSACTION
.
そして最後に、これまでのところあなたの変更点だけをロールバックするために:
-- Roll back changes...
IF @startingTranCount > 0
ROLLBACK TRANSACTION MySavePointName
ELSE
ROLLBACK TRANSACTION
. 他のヒント
extending Brian Bの答え
これにより、セーブポイント名が一意であり、SQL Server 2012の新しいTry / Catch / Throp機能を使用します。
DECLARE @mark CHAR(32) = replace(newid(), '-', '');
DECLARE @trans INT = @@TRANCOUNT;
IF @trans = 0
BEGIN TRANSACTION @mark;
ELSE
SAVE TRANSACTION @mark;
BEGIN TRY
-- do work here
IF @trans = 0
COMMIT TRANSACTION @mark;
END TRY
BEGIN CATCH
IF xact_state() = 1 OR (@trans = 0 AND xact_state() <> 0) ROLLBACK TRANSACTION @mark;
THROW;
END CATCH
. 私のストアドプロシージャにこのタイプのトランザクションマネージャを使用しました:
CREATE PROCEDURE Ardi_Sample_Test
@InputCandidateID INT
AS
DECLARE @TranCounter INT;
SET @TranCounter = @@TRANCOUNT;
IF @TranCounter > 0
SAVE TRANSACTION ProcedureSave;
ELSE
BEGIN TRANSACTION;
BEGIN TRY
/*
<Your Code>
*/
IF @TranCounter = 0
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF @TranCounter = 0
ROLLBACK TRANSACTION;
ELSE
IF XACT_STATE() <> -1
ROLLBACK TRANSACTION ProcedureSave;
DECLARE @ErrorMessage NVARCHAR(4000);
DECLARE @ErrorSeverity INT;
DECLARE @ErrorState INT;
SELECT @ErrorMessage = ERROR_MESSAGE();
SELECT @ErrorSeverity = ERROR_SEVERITY();
SELECT @ErrorState = ERROR_STATE();
RAISERROR (@ErrorMessage, @ErrorSeverity, @ErrorState);
END CATCH
GO
.