EXECUTE後のトランザクション数は、BEGINステートメントとCOMMITステートメントの数が一致しないことを示しています。以前のカウント= 1、現在のカウント= 0


94

私が持っているInsertにデータを送りますストアドプロシージャTable1とget Column1から値をTable1と表2を養う二ストアドプロシージャを呼び出すを。

しかし、2番目のストアドプロシージャを次のように呼び出すと、

Exec USPStoredProcName

次のエラーが発生します。

EXECUTE後のトランザクション数は、BEGINステートメントとCOMMITステートメントの数が一致しないことを示しています。以前のカウント= 1、現在のカウント= 0。

私は他のそのような質問の答えを読みましたが、正確にコミット数がめちゃくちゃになっている場所を見つけることができません。


手順にTRY / CATCHブロックがありますか?
Remus Rusanu 2014

はい、TRY / CATCHブロックがあります
Vignesh Kumar A 14

回答:


109

TRY / CATCHブロックがある場合、考えられる原因は、トランザクションアボート例外をキャッチして続行していることです。CATCHブロックでは、常にを確認しXACT_STATE()、適切に中止されコミットできない(終了した)トランザクションを処理する必要があります。呼び出し元がトランザクションを開始し、caleeが(トランザクションを中止した)デッドロックにヒットした場合、呼び出し先は、トランザクションが中止され、「通常どおりのビジネス」を続行すべきでないことを呼び出し元にどのように伝えますか?実行可能な唯一の方法は、例外を再度発生させ、呼び出し側に状況を処理させることです。中止されたトランザクションを黙って飲み込み、呼び出し元がまだ元のトランザクションにいると想定し続ける場合、確認できるのは騒乱だけです(そして、エラーは、エンジンがそれ自体を保護しようとする方法です)。

ネストされたトランザクションと例外で使用できるパターンを示す例外処理とネストされたトランザクション検討することをお勧めします。

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
go

3
ご協力いただきありがとうございます。Raiserrorを使用して問題を発見しました。それは、NOT NULLフィールドにNULL値を挿入しようとすることです
Vignesh Kumar A

ただし、制約チェックの検証ではトランザクションは中止されません。キャッチで明示的にロールバックしていますxact_abort onか、それとも使用していますか?
Remus Rusanu 2014

明示的にロールバックする
Vignesh Kumar A 14

2
私はこのパターンを試しましたが、それでも機能しません-外部トランザクションがある場合、このパターンはセーブポイントを作成し、重大なエラー(不快なトランザクション)の場合は外部トランザクションをロールバックします-これにより、入力する前に@@ trancount = 1が発生しますプロシージャと終了時に@@ trancount = 0
スズメ

3
CATCHのこのビットは間違っていると思います。このMSDNの例をif @xstate = -1 rollback; 見ると、外部トランザクションがなかった場合(つまり、そうでない場合)を除いて、トランザクション全体をロールバックしないでください。この手順は、@ sparrowの問題を修正するトランザクションを開始した場合にのみ行うべきだと思います。begin tranrollback
Nick

59

私もこの問題を抱えていました。私にとって、その理由は

return
commit

の代わりに

commit
return   

1つのストアドプロシージャ。


4
@seguso-これは非常に役に立ちました。共有していただきありがとうございます。時には、ほんの少し下にあるものがほこりの下に入ります。最高の状態になります。
レオグルディアン2017年

これは私にとって問題でしたが、データアクセスレイヤーを介して1つの大きなトランザクションで複数のsproc呼び出しをラップしていたため、それほど明白ではありませんでした。この問題が発生した場合は、トランザクションを作成しているsproc自体の外部に何かがないことを確認してください。ある場合は、sproc内でreturnステートメントをまったく使用できない可能性があります。
EF0

これは私でした。トランザクションがあり、コミットトランザクションの前にif / elseステートメントで戻ってきました
Kevin

18

これは通常、トランザクションが開始され、コミットされていないか、ロールバックされていない場合に発生します。

ストアドプロシージャでエラーが発生した場合、例外処理がないと、いくつかのランタイムエラーが原因でトランザクションが完了しないため、データベーステーブルがロックされる可能性があります。以下のような例外処理を使用できます。 SET XACT_ABORT

SET XACT_ABORT ON
SET NoCount ON
Begin Try 
     BEGIN TRANSACTION 
        //Insert ,update queries    
     COMMIT
End Try 
Begin Catch 
     ROLLBACK
End Catch

ソース


これが事実である場合、引用された質問/回答はおそらくこれを重複としてマークしてクローズする必要があることを意味するはずです
Mark Schultheiss

10

ネストされたトランザクションを使用する場合、ROLLBACK操作は最も外側のトランザクションを含むすべてのネストされたトランザクションをロールバックすることに注意してください。

これをTRY / CATCHと組み合わせて使用​​すると、説明したエラーが発生する可能性があります。詳細はこちら


5

これは、トランザクションを開いた後にストアドプロシージャでコンパイルエラーが発生した場合にも発生します(たとえば、テーブルが見つからない、列名が無効)。

私は2つのストアドプロシージャを使用する必要があることに気付きました。「ワーカー」と、Remus Rusanuによって概説されているものと同様のロジックを持つtry / catchを使用したラッパーです。ワーカーキャッチは「通常の」エラーを処理するために使用され、ラッパーキャッチはコンパイルエラーを処理するために使用されます。

https://msdn.microsoft.com/en-us/library/ms175976.aspx

TRY…CATCH構成の影響を受けないエラー

次のタイプのエラーは、TRY…CATCHコンストラクトと同じ実行レベルで発生した場合、CATCHブロックでは処理されません。

  • 構文エラーなど、バッチの実行を妨げるコンパイルエラー
  • 名前解決の遅延のためにコンパイル後に発生するオブジェクト名解決エラーなど、ステートメントレベルの再コンパイル中に発生するエラー。

うまくいけば、これは誰かがデバッグの数時間を節約するのに役立ちます...


1
ジャスティンに感謝します。素晴らしい観察。私の場合、SPの保存中にコンパイルエラーを生成しない更新内で集計を行っていましたが、実際には無効な構文でした-「UPDATEステートメントのセットリストに集計が表示されない可能性があります」
kuklei

4

私の場合、エラーは内で発生してRETURNいましたBEGIN TRANSACTION。だから私はこのようなものを持っていました:

Begin Transaction
 If (@something = 'foo')
 Begin
     --- do some stuff
     Return
 End
commit

そしてそれはする必要があります:

Begin Transaction
 If (@something = 'foo')
 Begin
     --- do some stuff
     Rollback Transaction ----- THIS WAS MISSING
     Return
 End
commit

2

私にとって大規模なデバッグの後、修正は単純な欠落でした。ロールバック後のキャッチ内のステートメント。これがないと、この醜いエラーメッセージが最終的に表示されます。

begin catch
    if @@trancount > 0 rollback transaction;
    throw; --allows capture of useful info when an exception happens within the transaction
end catch

2

同じエラーメッセージが表示されましたが、COMMIT TRANSACTION行の最後にセミコロンが付いていました


このように単純です。さらに、SPが完全に実行されない場合に備えて、私のケースでは「ROLLBACK」ステートメントが必要でした。トランザクションを閉じる/終了するためだけです。
J Cordero 2018年

1

このステートメントをトランザクションから省略した後、このエラーが1回発生しました。

COMMIT TRANSACTION [MyTransactionName]

1

私の意見では、受け入れられた答えはほとんどの場合過剰です。

エラーの原因は、エラーで明確に示されているように、BEGINとCOMMITの不一致であることがよくあります。つまり、以下を使用します。

Begin
  Begin
    -- your query here
  End
commit

の代わりに

Begin Transaction
  Begin
    -- your query here
  End
commit

開始後にトランザクションを省略すると、このエラーが発生します。


1

1つ以上がコミットされていないまま、同じプロシージャ/クエリに複数のトランザクションがないことを確認してください。

私の場合、クエリに誤ってBEGIN TRANステートメントが含まれていました


1

これは、C#コードからSPを呼び出す方法にも依存します。SPがテーブルタイプの値を返す場合は、ExecuteStoreQueryを使用してSPを呼び出し、SPが値を返さない場合は、ExecuteStoreCommandを使用してSPを呼び出します。


1

使用を避ける

RETURN

使用時のステートメント

BEGIN TRY
    ... 
END TRY

BEGIN CATCH
    ...
END CATCH

そして

BEGIN, COMMIT & ROLLBACK

SQLストアドプロシージャのステートメント


0

次のようなコード構造がある場合:

SELECT 151
RETURN -151

次に使用します:

SELECT 151
ROLLBACK
RETURN -151
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.