2016-05-06 15 views
6

私はより多くのTSQLを学ぶC#開発者です。私はこのようなスクリプトを書きました:トランザクションでtry catchが必要ですか?

begin transaction 
--Insert into several tables 
end transaction 

をしかし、私はそれは良いアイデアではなかったし、このようなものを使用するように言われました:

BEGIN TRANSACTION; 

BEGIN TRY 
    -- Generate a constraint violation error. 
    DELETE FROM Production.Product 
    WHERE ProductID = 980; 
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 

2番目の例では、より正確である私はなぜ表示されません。最初のものは同じように動作しませんか?最初のものはすべてのテーブルを更新するか、まったく更新しないようですか?コミットする前に@@TRANCOUNTのチェックが必要なのはなぜわかりません。

+1

私はあなたと同じ議論をします。さらに、try/catchパターンは、私がtry/squelchと呼ぶ反パターンです。それはキャプチャし、エラーが発生し、次に黙っていきます。それはエラーを処理するものではなく、それらを抑止しています。トランザクションにはtry/catchブロックは必要ないと言われています。特にトリガをしている場合は、try/catchを使用すると、これまで以上に多くの問題が発生する可能性があります。 –

+2

2番目の例の何かがあれば、catchはcatchブロックの後ではなくtryブロック内になければなりません。 – Kritner

答えて

4

tryブロック内で実際のステートメントの直前にトランザクションを開くだけです。すぐにコミットしてください。トランザクションをコミットするためにバッチの最後に移動するのを待つ必要はありません。

Try Blockに入ってトランザクションを開いたときに何か問題が発生した場合コントロールがCATCHブロックにジャンプし、そこでトランザクションをロールバックし、必要に応じて他のエラー処理を行います。

実際に@@ ROWCOUNT関数を使用して、開いているトランザクションのトランザクションチェックを実際にロールバックする前に、少しのチェックを追加しました。 paramの値やその他のものをチェックしたり、検証チェックが失敗した場合にtryブロックでエラーを発生させるなど、トランザクションを開く前にtryブロックでいくつかの検証チェックを行っていると便利です。その場合、コントロールはcatchブロックにジャンプしますトランザクションを開いていなくても、開いているトランザクションがあるかどうかをチェックし、開いているトランザクションがある場合はロールバックすることができます。あなたのケースでは、トランザクション内で何かがうまくいかないかぎり、キャッチブロックにアクセスしないので、開いているトランザクションを確認する必要はありません。

BEGIN TRY 

    BEGIN TRANSACTION 
    -- Multiple Inserts 
    INSERT INTO.... 
    INSERT INTO.... 
    INSERT INTO.... 
COMMIT TRANSACTION 
    PRINT 'Rows inserted successfully...' 

END TRY 

BEGIN CATCH 
    IF (@@TRANCOUNT > 0) 
    BEGIN 
     ROLLBACK TRANSACTION 
     PRINT 'Error detected, all changes reversed' 
    END 
    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 
END CATCH 
+0

@@ RowCountではなく、あなたの説明に '@@ TranCount'を意味すると思います。私はまた、 "失われた"アイデンティティ値、トリガーによって引き起こされる副作用があるかもしれないので、 "すべての変更が逆転しました"という一般的なメッセージに含めることを躊躇します。 – HABO

0

私はT-SQLのTRY ... CATCHについて2つの考えを持っています。

言語に潜在的に有用な追加機能ですが、利用可能であるという事実は必ずしもそれを使用する理由ではありません。

(Iは一例で実現)あなたの

DELETE FROM Table WHERE... 

を例に取ります。エラーが発生して失敗する唯一の方法は、コードがスキーマから大きく外れている場合です。 (例えば、誰かがテーブルのPK終わりに外部キーを作成した場合)。

適切なテストが行​​われた場合、コードとスキーマの間のこのような不一致は、決して生産にはならないはずです。それがIMHOであると仮定すると、IMHOは、クライアントに返すためのSELECTステートメントへの "丁寧な"ラッピングよりも、何がうまくいかないのかをより正確に示すことになります。 (これはtry/squelch反パターンのSeanLangeに言及することができます)。

もっと複雑なシナリオでは、TRY ... CATCHの使用がわかります。 IMHOでは、入力パラメータの慎重な検証に代わるものではありません。

1

私はC#の開発者としてここに私の視点を与えたいと思う:

(単にスクリプトからいくつかのテーブルに挿入する)上述の単純なシナリオでは、のtry/catchを追加する理由はありません、などこのトランザクションには何のメリットもありません。どちらの例でも、全く同じ結果が得られます。すべてのテーブルが挿入されるか、または挿入されません。データベースの状態は一貫しています。(COMMIT TRANSACTIONは呼び出されないので、スクリプトの最後にSql Serverによってロールバックが暗黙的に呼び出されます)。

ただし、try/catchの中で、統合されていないエラー処理。たとえば、エラーテーブルにエラーを記録します。

私のC#の経験では、Try/Catchを使用する唯一の時間は、ファイルを開こうとするなど、開発者のコ​​ントロールの外にあるときです。そのような場合、.Netフレームワークによって生成された例外を管理する唯一の方法は、Try/Catchによるものです。

私がストアドプロシージャを実行していて、手動でデータの状態を確認して手動でROLLBACK TRANSACTIONを呼び出す場合、私はそれを見ることができました。しかし、それでもtry/catchは必要ありません。

関連する問題