我们使用基于http://www.sommarskog.se/error_handling/Part1.html 的通用错误处理程序,我们在适用时将其包含在我们的(嵌套)事务中,以确保正确管理链:
CREATE PROCEDURE [dbo].[sp_ErrorHandler](@caller VARCHAR(255))
AS BEGIN
SET NOCOUNT ON;
DECLARE @errmsg NVARCHAR(2048), @severity TINYINT, @state TINYINT, @errno INT, @lineno INT;
SELECT @errmsg=REPLACE(ERROR_MESSAGE(), 'DatabaseException: ', 'DatabaseException: '+QUOTENAME(@caller)+' --> ')
, @severity=ERROR_SEVERITY()
, @state=ERROR_STATE()
, @errno=ERROR_NUMBER()
, @lineno=ERROR_LINE();
IF @errmsg NOT LIKE 'DatabaseException%' BEGIN
SELECT @errmsg=N'DatabaseException: '+QUOTENAME(@caller)+N', Line '+LTRIM(STR(@lineno))+N', Error '+LTRIM(STR(@errno))+N': '+@errmsg;
END;
RAISERROR('%s', @severity, @state, @errmsg);
END;
(在master数据库中编译并标记为系统过程)
我们使用这个错误处理程序如下。在演示中,我有一个外部 proc 和一个内部 proc 都使用事务。
CREATE PROCEDURE dbo.uspOuterProc
AS
BEGIN
SET NOCOUNT, XACT_ABORT ON;
BEGIN TRY
BEGIN TRANSACTION;
EXEC dbo.uspInnerProc;
PRINT 1;
COMMIT;
END TRY
BEGIN CATCH
IF @@trancount > 0
ROLLBACK TRANSACTION;
EXEC master.dbo.sp_ErrorHandler @caller = 'dbo.uspOuterProc';
END CATCH;
END;
GO
CREATE PROCEDURE dbo.uspInnerProc
AS
BEGIN
SET NOCOUNT, XACT_ABORT ON;
BEGIN TRY
BEGIN TRANSACTION;
PRINT 2;
SELECT 1 / 0;
PRINT 3;
COMMIT;
END TRY
BEGIN CATCH
IF @@trancount > 0
ROLLBACK TRANSACTION;
EXEC master.dbo.sp_ErrorHandler @caller = 'dbo.uspInnerProc';
END CATCH;
END;
GO
编译并运行后:
EXEC dbo.uspOuterProc
你应该得到这个结果:
2
Msg 50000, Level 16, State 1, Procedure sp_ErrorHandler, Line 13 [Batch Start Line 48]
DatabaseException: [dbo.uspOuterProc] --> [dbo.uspInnerProc], Line 12, Error 8134: Divide by zero error encountered.