歴史的に見ると、SQLでのエラー処理は致命的なエラーの後にトランザクションをロールバックすることに限定されていました。しかし、ビジネスロジックの発展に伴い、エラーの正確なログ記録、変更が行われた部分のみのロールバック、さらにプロシージャの呼び出しのネスト(nested transactions)の可能性が求められるようになりました。
問題は、すべてのDBMSがネストされたトランザクションを完全にサポートしているわけではないことです(例:MS SQL Serverでは、実際のトランザクションではなくSAVEPOINTの保存したポイントレベルに近いものです)。また、エラー時の例外が必要なクリーンアップまで実行を中断させることがあります。TRY/CATCHなどのメカニズムが使用されていない場合には特に。
解決策:TRY/CATCH(または類似の構文)を使用し、ネストに対してSAVEPOINTを用いると共に、エラー時のプロシージャの動作を設定します(例:SQL ServerではSET XACT_ABORT)。
コードの例(MS SQL Server):
BEGIN TRY BEGIN TRANSACTION -- 操作 EXEC dbo.InnerProcedure COMMIT TRANSACTION END TRY BEGIN CATCH IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION -- エラーログの記録 INSERT INTO error_log VALUES (ERROR_NUMBER(), ERROR_MESSAGE(), GETDATE()) END CATCH
主な特性:
ネストされたトランザクションでエラーが発生した場合、すべての変更が実際にロールバックされるのですか?
ほとんどのDBMSでは、ネストされたトランザクションは独立したトランザクションではなくライブラリポイント(SAVEPOINT)です。全体のロールバックが行われると、すべての変更が最初の(外側の)トランザクションまでロールバックされます。
@@TRANCOUNTを確認せずにアクティブなトランザクション以外でROLLBACKを実行するとどうなりますか?
エラーが発生します-ロールバックのためのアクティブなトランザクションがありません。ROLLBACKの前に常に@@TRANCOUNT > 0を確認してください。
TRY/CATCH内でトリガーやカーソルは機能しますか?
はい、ただしトリガー内のエラーはCATCHに遷移し、全体のトランザクションをロールバックする可能性があります。プロシージャ本体と呼び出されるオブジェクト(プロシージャ、トリガー)の両方でエラー処理を考慮する必要があります。
いくつかのネストされたプロシージャがあり、各プロシージャがTRY/CATCHなしでBEGIN TRANSACTION / COMMITを使用している場合、エラーが発生すると一部のデータが変更され、一部が変更されない状態になります。
利点:
欠点:
全てのロジックがTRY/CATCHでラップされ、ネストされたプロシージャはSAVEPOINTを使用するか、1つの外部トランザクション内で操作し、すべてのエラーがログ記録されます。
利点:
欠点: