web-dev-qa-db-ja.com

sql try / catch rollback / commit-ロールバック後の誤ったコミットの防止

トランザクションとtry/catchブロックを持つMS SQLスクリプトを記述しようとしています。例外をキャッチすると、トランザクションはロールバックされます。そうでない場合、トランザクションはコミットされます。私はこのようにすることを言っているいくつかの異なるウェブサイトを見ました:

begin transaction
begin try
    --main content of script here
end try
begin catch
    rollback transaction
end catch

commit transaction

しかし、例外をキャッチした場合でも、「コミットトランザクション」行にヒットしませんか?トランザクションはすでにロールバックされているため、これはSQLエラーにつながりませんか?私はそれが次のように行われるべきだと思う:

declare @success bit = 1

begin transaction
begin try
    --main content of script here
end try
begin catch
    rollback transaction
    set @success = 0
end catch

if(@success = 1)
begin
    commit transaction
end

よく投稿されるソリューションに@success変数が含まれていないのはどうしてですか?すでにロールバックされたトランザクションをコミットした結果として発生するSQLエラーはありませんか?例外をキャッチした場合でも、最初のコード例の「トランザクションのコミット」行がヒットするというのは間違っていますか?

46
user3666839

私はいつも これはより良い記事の1つでした について考えました。私はそれを明確にするだろうと思う次の例を含み、信頼できるネストされたトランザクションに必要な見落とされがちな@@ trancountを含む

PRINT 'BEFORE TRY'
BEGIN TRY
    BEGIN TRAN
     PRINT 'First Statement in the TRY block'
     INSERT INTO dbo.Account(AccountId, Name , Balance) VALUES(1, 'Account1',  10000)
     UPDATE dbo.Account SET Balance = Balance + CAST('TEN THOUSAND' AS MONEY) WHERE AccountId = 1
     INSERT INTO dbo.Account(AccountId, Name , Balance) VALUES(2, 'Account2',  20000)
     PRINT 'Last Statement in the TRY block'
    COMMIT TRAN
END TRY
BEGIN CATCH
    PRINT 'In CATCH Block'
    IF(@@TRANCOUNT > 0)
        ROLLBACK TRAN;

    THROW; -- raise error to the client
END CATCH
PRINT 'After END CATCH'
SELECT * FROM dbo.Account WITH(NOLOCK)
GO
70
Gary Walker

最初の例では、あなたは正しいです。 tryブロックが起動するかどうかに関係なく、バッチはコミットトランザクションをヒットします。

2番目の例では、他のコメント者に同意します。成功フラグを使用する必要はありません。

基本的に、次のアプローチは軽量のベストプラクティスアプローチであると考えています。

例外の処理方法を確認するには、2番目の挿入の値を255から256に変更します。

CREATE TABLE #TEMP ( ID TINYINT NOT NULL );
INSERT  INTO #TEMP( ID ) VALUES  ( 1 )

BEGIN TRY
    BEGIN TRANSACTION

    INSERT  INTO #TEMP( ID ) VALUES  ( 2 )
    INSERT  INTO #TEMP( ID ) VALUES  ( 255 )

    COMMIT TRANSACTION
END TRY
BEGIN CATCH
    DECLARE 
        @ErrorMessage NVARCHAR(4000),
        @ErrorSeverity INT,
        @ErrorState INT;
    SELECT 
        @ErrorMessage = ERROR_MESSAGE(),
        @ErrorSeverity = ERROR_SEVERITY(),
        @ErrorState = ERROR_STATE();
    RAISERROR (
        @ErrorMessage,
        @ErrorSeverity,
        @ErrorState    
        );
    ROLLBACK TRANSACTION
END CATCH

SET NOCOUNT ON

SELECT ID
FROM #TEMP

DROP TABLE #TEMP
30
Jim V.

Try-CatchCommit Transaction- Rollback TransactionError Trackingを使用するms sqlスクリプトパターンを数回正常に使用しました。

TRYブロックは次のようになります

 BEGIN TRY
 BEGIN TRANSACTION T
 ----
 //your script block
 ----
 COMMIT TRANSACTION T 
 END TRY

CATCHブロックは次のようになります

BEGIN CATCH
DECLARE @ErrMsg NVarChar(4000), 
        @ErrNum Int, 
        @ErrSeverity Int, 
        @ErrState Int, 
        @ErrLine Int, 
        @ErrProc NVarChar(200)
 SELECT @ErrNum = Error_Number(), 
       @ErrSeverity = Error_Severity(), 
       @ErrState = Error_State(), 
       @ErrLine = Error_Line(), 
       @ErrProc = IsNull(Error_Procedure(), '-')
 SET @ErrMsg = N'ErrLine: ' + rtrim(@ErrLine) + ', proc: ' + RTRIM(@ErrProc) + ', 
       Message: '+ Error_Message()

ROLLBACKスクリプトは、次のようにCATCHブロックの一部になります

IF (@@TRANCOUNT) > 0 
BEGIN
PRINT 'ROLLBACK: ' + SUBSTRING(@ErrMsg,1,4000)
ROLLBACK TRANSACTION T
END
ELSE
BEGIN
PRINT SUBSTRING(@ErrMsg,1,4000);   
END

END CATCH

上記の異なるスクリプトブロックは、1つのブロックとして使用する必要があります。 TRYブロックでエラーが発生した場合、CATCHブロックに進みます。そこでは、エラー番号、エラーの重大度、エラー行などに関するさまざまな詳細を設定しています。最後に、これらすべての詳細が@ErrMsgパラメーターに追加されます。次に、トランザクションのカウント(@@ TRANCOUNT> 0)をチェックします。つまり、ロールバックのためにトランザクションに何かが存在するかどうかをチェックします。存在する場合は、エラーメッセージとROLLBACK TRANSACTIONを表示します。それ以外の場合は、単にエラーメッセージを出力します。

COMMIT TRANSACTION TスクリプトをTRYブロックの最後の行に向けて保持し、TRYブロック内のすべてのコードが完了した後にのみトランザクション(データベースの最終変更)をコミットするようにします正常に実行されます。

4
Rinoy Ashokan

取引カウンター

--@@TRANCOUNT = 0
begin try
--@@TRANCOUNT = 0
BEGIN TRANSACTION tran1
 --@@TRANCOUNT = 1

        --your code
        -- if failed  @@TRANCOUNT = 1
        -- if success @@TRANCOUNT = 0

COMMIT TRANSACTION tran1

end try

begin catch
    print 'FAILED'
end catch
0
Arun Prasad E S

以下が役に立つかもしれません。

ソース: https://msdn.Microsoft.com/en-us/library/ms175976.aspx

BEGIN TRANSACTION;

BEGIN TRY
    -- your code --
END TRY
BEGIN CATCH
    SELECT 
        ERROR_NUMBER() AS ErrorNumber
        ,ERROR_SEVERITY() AS ErrorSeverity
        ,ERROR_STATE() AS ErrorState
        ,ERROR_PROCEDURE() AS ErrorProcedure
        ,ERROR_LINE() AS ErrorLine
        ,ERROR_MESSAGE() AS ErrorMessage;

    IF @@TRANCOUNT > 0
        ROLLBACK TRANSACTION;
END CATCH;

IF @@TRANCOUNT > 0
    COMMIT TRANSACTION;
GO