ストアドプロシージャ内で普通にTRY...CATCHを使用するとそこでエラーを握りつぶしてしまい、呼び出し元アプリケーションにエラーがあったことが伝わらない。
例えば以下のように書くとエラーを握りつぶしてしまう。
BEGIN TRY
BEGIN TRANSACTION
--なんとかかんとか
COMMIT TRANSACTION
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION
END CATCH
この対処方法がBooks Onlineに載っている。
Transact-SQL での TRY...CATCH の使用
http://msdn2.microsoft.com/ja-jp/library/ms179296.aspx
の
「TRY...CATCH での RAISERROR の指定」に書かれている。
それによると、CATCHで捕まえた後、RAISERRORを利用してエラーを再スローしなきゃいけないようだ。
で、再スローするためのストアドプロシージャの例が載っている。これを以下に引用する。
CREATE PROCEDURE usp_RethrowError AS
-- Return if there is no error information to retrieve.
IF ERROR_NUMBER() IS NULL
RETURN;
DECLARE
@ErrorMessage NVARCHAR(4000),
@ErrorNumber INT,
@ErrorSeverity INT,
@ErrorState INT,
@ErrorLine INT,
@ErrorProcedure NVARCHAR(200);
-- Assign variables to error-handling functions that
-- capture information for RAISERROR.
SELECT
@ErrorNumber = ERROR_NUMBER(),
@ErrorSeverity = ERROR_SEVERITY(),
@ErrorState = ERROR_STATE(),
@ErrorLine = ERROR_LINE(),
@ErrorProcedure = ISNULL(ERROR_PROCEDURE(), '-');
-- Building the message string that will contain original
-- error information.
SELECT @ErrorMessage =
N'Error %d, Level %d, State %d, Procedure %s, Line %d, ' +
'Message: '+ ERROR_MESSAGE();
-- Raise an error: msg_str parameter of RAISERROR will contain
-- the original error information.
RAISERROR
(
@ErrorMessage,
@ErrorSeverity,
1,
@ErrorNumber, -- parameter: original error number.
@ErrorSeverity, -- parameter: original error severity.
@ErrorState, -- parameter: original error state.
@ErrorProcedure, -- parameter: original error procedure name.
@ErrorLine -- parameter: original error line number.
);
で、これを利用して以下のようにすると、ストアドプロシージャの呼び出し元アプリケーションにめでたくエラーが伝わる。
BEGIN TRY
BEGIN TRANSACTION
--なんとかかんとか
COMMIT TRANSACTION
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION
-- Call the procedure to raise the original error.
EXEC usp_RethrowError;
END CATCH
で、ここで疑問なのが、なぜusp_RethrowErrorなんていうストアドプロシージャをわざわざ作らなきゃならないのだろう?
最初からRETHROWERRORなんていうステートメントを用意しといてくれたらいいのに。