ストアドプロシージャを十分に堅牢にし、非常に適切にスケーリングし、エラー処理を含めるための良い方法は何ですか?
さらに、ストアドプロシージャで複数のエラーシナリオを処理し、呼び出し元のアプリに意味のあるエラー情報を返すインテリジェントフィードバックシステムを使用するための最良の方法は何ですか?
Alex Kuznetsovの著書Defensive Database Programming(第8章)には、T-SQL TRY ... CATCH、T-SQLトランザクションとSET XACT_ABORT設定、およびクライアント側のエラー処理の使用に関するすばらしい章があります。これは、どのオプションを実行する必要があるかについて最も意味のあるオプションを決定するのに役立ちます。
freethis site で利用できます。私はその会社とはまったく関係がありませんが、その本のハードコピー版を所有しています。
このテーマについては、アレックスが非常によく説明している細部がたくさんあります。
ニックの要求による...(しかし、このすべてが章にあるわけではありません)
スケーリングに関しては、dbコードに含める必要があるアクティビティとアプリに含める必要があるアクティビティについて、非常に正直である必要があります。高速に実行されるコードが、メソッドごとに単一の関心事の設計に戻る傾向があることに気付いたことがありますか?
通信する最も簡単な方法は、カスタムエラーコード(> 50,000)です。また、かなり高速です。それはあなたがdbコードとアプリコードを同期させておく必要があることを意味します。カスタムエラーコードを使用すると、エラーメッセージ文字列で有用な情報を返すこともできます。その状況に厳密に応じたエラーコードがあるため、エラーのデータ形式に合わせて調整されたアプリコードでパーサーを記述できます。
また、データベースで再試行ロジックが必要なエラー条件はどれですか。 X秒後に再試行する場合は、トランザクションがそれほどブロックされないように、アプリのコードでそれを処理することをお勧めします。 DML操作をすぐに再送信するだけの場合は、SPで繰り返すのが効率的です。ただし、コードを複製するか、再試行を実行するためのSPのレイヤー。
実際、それが現在SQL ServerのTRY ... CATCHロジックの最大の問題です。それはできますが、それは少し難解です。特に システム例外の再スロー (元のエラー番号を保持)で、SQL Server 2012でこれに加えられているいくつかの改善を探してください。また、 [〜#〜] formatmessage [〜#〜] があり、特にロギングの目的で、エラーメッセージを作成する際に柔軟性が追加されています。
これは私たちのテンプレートです(エラーログは削除されました)
ノート:
...したがって、必要以上のTXNを作成しないでください
しかしながら、
CREATE PROCEDURE [Name]
AS
SET XACT_ABORT, NOCOUNT ON
DECLARE @starttrancount int
BEGIN TRY
SELECT @starttrancount = @@TRANCOUNT
IF @starttrancount = 0
BEGIN TRANSACTION
[...Perform work, call nested procedures...]
IF @starttrancount = 0
COMMIT TRANSACTION
END TRY
BEGIN CATCH
IF XACT_STATE() <> 0 AND @starttrancount = 0
ROLLBACK TRANSACTION
RAISERROR [rethrow caught error using @ErrorNumber, @ErrorMessage, etc]
END CATCH
GO
私はTry/Catchを使用していますが、できるだけ多くの情報を収集し、ロールバック後にエラーログに書き込みます。この例では、「LogEvent」は、発生したイベントの詳細を含む、EventLogテーブルに書き込むストアドプロシージャです。 GetErrorInfo()は、正確なエラーメッセージを返す関数呼び出しです。
エラーが発生すると、情報が収集され、手順はエラー処理セクションまでスキップしてロールバックを発行します。情報がログに書き込まれた後、プロシージャは終了します。
追加のプロシージャ/関数呼び出しが含まれていることを考えると、それは少し上回っています。ただし、この方法は、問題をデバッグするときに非常に役立ちます。
exec LogEvent @Process、@Database、 'Attinging to insert blah blah blah' BEGIN TRY insert into MyTable select Values from MyOtherTable select @rowcount = @@ ROWCOUNT END TRY -ErrorHandling BEGIN CATCH select @error = ERROR_NUMBER( )、 @rowcount = -1、 @TableAction = 'insert'、 @TableName = @Database + '.MyTable'、 @AdditionalInfo = ' (何とか何とか何とか挿入しようとしています) '+ dbo.GetErrorInfo() GOTO TableAccessError END CATCH 。 。 。 。 TableAccessError: IF(@@ TRANCOUNT> 0)ROLLBACK select @output = upper(@TableAction)+ 'エラー-' + ケース(@TableAction) の実行中にエラーが発生しました。 .____。] else @TableAction + 'ing ' end + ' records '+ case(@TableAction) when' select 'then' from ' when' update 'then 'in' when 'insert' then 'into' else 'from' end + '' + @TableName + 'table。' select @output = @output + '@@ ERROR:' + convert(varchar(8)、@ error) select @output = @output + '@@ ROWCOUNT:' + convert(varchar( 8)、@ rowcount) select @output = @output + isnull(@AdditionalInfo、 '') exec LogEvent @Process、@Database、@Output RAISERROR(@ output、16,1)with log select @ReturnCode = -1 GOTO THE_EXIT