SQL Server 2008 有一个新的 try/catch 结构。如果我在一系列嵌套存储过程中遇到错误,我喜欢将调用堆栈记录在错误表中。问题是,如果我启动了一个事务(对于更新数据库的操作来说是这样),当 try/catch 语句的 catch 部分中的代码回滚事务时,写入错误表的记录将被删除.
欢迎任何关于我如何解决这个问题的提示。
SQL Server 2008 有一个新的 try/catch 结构。如果我在一系列嵌套存储过程中遇到错误,我喜欢将调用堆栈记录在错误表中。问题是,如果我启动了一个事务(对于更新数据库的操作来说是这样),当 try/catch 语句的 catch 部分中的代码回滚事务时,写入错误表的记录将被删除.
欢迎任何关于我如何解决这个问题的提示。
一些选项:
SO 关于 SET XACT_ABORT ON 的问题(带有一些错误处理)
这将创建两个测试表和三个嵌套过程,其中包含事务:
if exists (select * from sysobjects where id = object_id(N'[dbo].ProcedureA') and OBJECTPROPERTY(id, N'IsProcedure') = 1) drop procedure [dbo].ProcedureA
if exists (select * from sysobjects where id = object_id(N'[dbo].ProcedureB') and OBJECTPROPERTY(id, N'IsProcedure') = 1) drop procedure [dbo].ProcedureB
if exists (select * from sysobjects where id = object_id(N'[dbo].ProcedureC') and OBJECTPROPERTY(id, N'IsProcedure') = 1) drop procedure [dbo].ProcedureC
if exists (select * from sysobjects where id = object_id(N'YourLogTable') and OBJECTPROPERTY(id, N'IsTable') = 1) drop table YourLogTable
if exists (select * from sysobjects where id = object_id(N'YourTestTable') and OBJECTPROPERTY(id, N'IsTable') = 1) drop table YourTestTable
go
CREATE TABLE YourLogTable
(
LogID int not null primary key identity(1,1)
,LogDate datetime not null default GETDATE()
,ProcedureName varchar(50) null default OBJECT_NAME(@@PROCID)
,LogText varchar(8000) not null
)
go
CREATE TABLE YourTestTable
(
TestID int not null primary key identity(1,1)
,TestData varchar(100) not null
)
go
-----------------------------------------------------------------------
-----------------------------------------------------------------------
CREATE PROCEDURE ProcedureA
(
@ParamA1 int
,@ParamA2 varchar(10)
,@ErrorMsg varchar(1000) OUTPUT
)
AS
DECLARE @LogValue varchar(8000)
DECLARE @ReturnValueX int
DECLARE @ErrorMsgX varchar(1000)
DECLARE @TempValue int
BEGIN TRY
SET @LogValue=ISNULL(OBJECT_NAME(@@PROCID), 'unknown')
+': @ParamA1='+COALESCE(''''+CONVERT(varchar(10),@ParamA1)+'''','null')
+', @ParamA2='+COALESCE(''''+@ParamA2+'''','null')
BEGIN TRANSACTION --<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<,
INSERT INTO YourTestTable (TestData) VALUES ('I was in top of ProdecureA')
--your logic logic here---
IF @ParamA1=1
BEGIN
RAISERROR('testing, bad parameter',16,1) --send control to the BEGIN CATCH block
END
SET @TempValue=@ParamA1/@ParamA1
EXEC @ReturnValueX=ProcedureB @ParamA1,@ParamA2,@ErrorMsgX OUTPUT
IF @ReturnValueX!=0
BEGIN
SET @ErrorMsg='Call to ProcedureB failed, ReturnValueX='+COALESCE(''''+CONVERT(varchar(10),@ReturnValueX)+'''','null')+', @ErrorMsgX='+COALESCE(''''+CONVERT(varchar(10),@ErrorMsgX)+'''','null')
RAISERROR(@ErrorMsg,16,1) --send control to the BEGIN CATCH block
END
--your logic logic here---
INSERT INTO YourTestTable (TestData) VALUES ('I was in bottom of ProdecureA')
END TRY
BEGIN CATCH
IF XACT_STATE()!=0
BEGIN
ROLLBACK TRANSACTION
END
SET @ErrorMsg=@LogValue+'; '
+CASE WHEN ERROR_NUMBER() IS NOT NULL THEN 'Msg ' +CONVERT(varchar(30), ERROR_NUMBER() ) ELSE '' END
+CASE WHEN ERROR_SEVERITY() IS NOT NULL THEN ', Level ' +CONVERT(varchar(30), ERROR_SEVERITY() ) ELSE '' END
+CASE WHEN ERROR_STATE() IS NOT NULL THEN ', State ' +CONVERT(varchar(30), ERROR_STATE() ) ELSE '' END
+CASE WHEN ERROR_PROCEDURE() IS NOT NULL THEN ', Procedure ' + ERROR_PROCEDURE() ELSE '' END
+CASE WHEN ERROR_LINE() IS NOT NULL THEN ', Line ' +CONVERT(varchar(30), ERROR_LINE() ) ELSE '' END
+CASE WHEN ERROR_MESSAGE() IS NOT NULL THEN ', ' + ERROR_MESSAGE() ELSE '' END
INSERT INTO YourLogTable (LogText) VALUES (@ErrorMsg)
RETURN 999
END CATCH
IF XACT_STATE()!=0
BEGIN
COMMIT
END
--PRINT ISNULL(OBJECT_NAME(@@PROCID), 'unknown')+' OK'
RETURN 0
GO
-----------------------------------------------------------------------
-----------------------------------------------------------------------
CREATE PROCEDURE ProcedureB
(
@ParamB1 int
,@ParamB2 varchar(10)
,@ErrorMsg varchar(1000) OUTPUT
)
AS
DECLARE @LogValue varchar(8000)
DECLARE @ReturnValueX int
DECLARE @ErrorMsgX varchar(1000)
DECLARE @TempValue int
BEGIN TRY
SET @LogValue=ISNULL(OBJECT_NAME(@@PROCID), 'unknown')
+': @ParamB1='+COALESCE(''''+CONVERT(varchar(10),@ParamB1)+'''','null')
+', @ParamB2='+COALESCE(''''+@ParamB2+'''','null')
BEGIN TRANSACTION --<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<,
INSERT INTO YourTestTable (TestData) VALUES ('I was in top of ProdecureB')
--your logic logic here---
IF @ParamB1=10
BEGIN
RAISERROR('testing, bad parameter',16,1) --send control to the BEGIN CATCH block
END
SET @TempValue=@ParamB1/@ParamB1
EXEC @ReturnValueX=ProcedureC @ParamB1,@ErrorMsgX OUTPUT
IF @ReturnValueX!=0
BEGIN
SET @ErrorMsg='Call to ProcedureC failed, ReturnValueX='+COALESCE(''''+CONVERT(varchar(10),@ReturnValueX)+'''','null')+', @ErrorMsgX='+COALESCE(''''+CONVERT(varchar(10),@ErrorMsgX)+'''','null')
RAISERROR(@ErrorMsg,16,1) --send control to the BEGIN CATCH block
END
--your logic logic here---
INSERT INTO YourTestTable (TestData) VALUES ('I was in bottom of ProdecureB')
END TRY
BEGIN CATCH
IF XACT_STATE()!=0
BEGIN
ROLLBACK TRANSACTION
END
SET @ErrorMsg=@LogValue+'; '
+CASE WHEN ERROR_NUMBER() IS NOT NULL THEN 'Msg ' +CONVERT(varchar(30), ERROR_NUMBER() ) ELSE '' END
+CASE WHEN ERROR_SEVERITY() IS NOT NULL THEN ', Level ' +CONVERT(varchar(30), ERROR_SEVERITY() ) ELSE '' END
+CASE WHEN ERROR_STATE() IS NOT NULL THEN ', State ' +CONVERT(varchar(30), ERROR_STATE() ) ELSE '' END
+CASE WHEN ERROR_PROCEDURE() IS NOT NULL THEN ', Procedure ' + ERROR_PROCEDURE() ELSE '' END
+CASE WHEN ERROR_LINE() IS NOT NULL THEN ', Line ' +CONVERT(varchar(30), ERROR_LINE() ) ELSE '' END
+CASE WHEN ERROR_MESSAGE() IS NOT NULL THEN ', ' + ERROR_MESSAGE() ELSE '' END
INSERT INTO YourLogTable (LogText) VALUES (@ErrorMsg)
RETURN 999
END CATCH
IF XACT_STATE()!=0
BEGIN
COMMIT
END
--PRINT ISNULL(OBJECT_NAME(@@PROCID), 'unknown')+' OK'
RETURN 0
GO
-----------------------------------------------------------------------
-----------------------------------------------------------------------
CREATE PROCEDURE ProcedureC
(
@ParamC1 int
,@ErrorMsg varchar(1000) OUTPUT
)
AS
DECLARE @LogValue varchar(8000)
DECLARE @ReturnValueX int
DECLARE @ErrorMsgX varchar(1000)
DECLARE @TempValue int
BEGIN TRY
BEGIN TRANSACTION --<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<,
SET @LogValue=ISNULL(OBJECT_NAME(@@PROCID), 'unknown')
+': @ParamC1='+COALESCE(''''+CONVERT(varchar(10),@ParamC1)+'''','null')
--your logic logic here---
INSERT INTO YourTestTable (TestData) VALUES ('I was in top of ProdecureC')
IF @ParamC1=100
BEGIN
RAISERROR('testing, bad parameter',16,1) --send control to the BEGIN CATCH block
END
SET @TempValue=@ParamC1/@ParamC1
--your logic logic here---
INSERT INTO YourTestTable (TestData) VALUES ('I was in bottom of ProdecureC')
END TRY
BEGIN CATCH
IF XACT_STATE()!=0
BEGIN
ROLLBACK TRANSACTION
END
SET @ErrorMsg=@LogValue+'; '
+CASE WHEN ERROR_NUMBER() IS NOT NULL THEN 'Msg ' +CONVERT(varchar(30), ERROR_NUMBER() ) ELSE '' END
+CASE WHEN ERROR_SEVERITY() IS NOT NULL THEN ', Level ' +CONVERT(varchar(30), ERROR_SEVERITY() ) ELSE '' END
+CASE WHEN ERROR_STATE() IS NOT NULL THEN ', State ' +CONVERT(varchar(30), ERROR_STATE() ) ELSE '' END
+CASE WHEN ERROR_PROCEDURE() IS NOT NULL THEN ', Procedure ' + ERROR_PROCEDURE() ELSE '' END
+CASE WHEN ERROR_LINE() IS NOT NULL THEN ', Line ' +CONVERT(varchar(30), ERROR_LINE() ) ELSE '' END
+CASE WHEN ERROR_MESSAGE() IS NOT NULL THEN ', ' + ERROR_MESSAGE() ELSE '' END
INSERT INTO YourLogTable (LogText) VALUES (@ErrorMsg)
RETURN 999
END CATCH
IF XACT_STATE()!=0
BEGIN
COMMIT
END
--PRINT ISNULL(OBJECT_NAME(@@PROCID), 'unknown')+' OK'
RETURN 0
GO
这将为四个测试用例运行上述过程:
代码在这里:
DECLARE @ReturnValue int
DECLARE @ErrorMsg varchar(1000)
print '###########################################################################'
print 'will error out in A - should log A>>> EXEC ProcedureA 1,''abcd'''
delete from YourTestTable; delete from YourLogTable;
EXEC @ReturnValue=ProcedureA 1,'abcd',@ErrorMsg OUTPUT; SELECT @ReturnValue AS '@ReturnValue' ,@ErrorMsg AS '@ErrorMsg'; select 'YourTestTable' AS 'YourTestTable',* from YourTestTable; select 'YourLogTable' AS 'YourLogTable',* from YourLogTable;
print '###########################################################################'
print 'will error out in B - should log B and A>>> EXEC ProcedureA 10,''abcd'''
delete from YourTestTable; delete from YourLogTable;
EXEC @ReturnValue=ProcedureA 10,'abcd',@ErrorMsg OUTPUT; SELECT @ReturnValue AS '@ReturnValue' ,@ErrorMsg AS '@ErrorMsg'; select 'YourTestTable' AS 'YourTestTable',* from YourTestTable; select 'YourLogTable' AS 'YourLogTable',* from YourLogTable;
print '###########################################################################'
print 'will error out in C - should log C, B and A>>>> EXEC ProcedureA 100,''123'''
delete from YourTestTable; delete from YourLogTable;
EXEC @ReturnValue=ProcedureA 100,'123',@ErrorMsg OUTPUT; SELECT @ReturnValue AS '@ReturnValue' ,@ErrorMsg AS '@ErrorMsg'; select 'YourTestTable' AS 'YourTestTable',* from YourTestTable; select 'YourLogTable' AS 'YourLogTable',* from YourLogTable;
print '###########################################################################'
print 'will complete, YourTestTable will contain data >>>> EXEC ProcedureA 2,''123'''
delete from YourTestTable; delete from YourLogTable;
EXEC @ReturnValue=ProcedureA 2,'123',@ErrorMsg OUTPUT; SELECT @ReturnValue AS '@ReturnValue' ,@ErrorMsg AS '@ErrorMsg'; select 'YourTestTable' AS 'YourTestTable',* from YourTestTable; select 'YourLogTable' AS 'YourLogTable',* from YourLogTable;
print '###########################################################################'
这是输出,它在日志中显示了一个堆栈,尽管有回滚:
###########################################################################
will error out in A - should log A>>> EXEC ProcedureA 1,'abcd'
(6 row(s) affected)
(0 row(s) affected)
(1 row(s) affected)
(1 row(s) affected)
@ReturnValue @ErrorMsg
------------ ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
999 ProcedureA: @ParamA1='1', @ParamA2='abcd'; Msg 50000, Level 16, State 1, Procedure ProcedureA, Line 33, testing, bad parameter
(1 row(s) affected)
YourTestTable TestID TestData
------------- ----------- ----------------------------------------------------------------------------------------------------
(0 row(s) affected)
YourLogTable LogID LogDate ProcedureName LogText
------------ ----------- ----------------------- -------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
YourLogTable 37 2009-09-08 15:04:39.663 ProcedureA ProcedureA: @ParamA1='1', @ParamA2='abcd'; Msg 50000, Level 16, State 1, Procedure ProcedureA, Line 33, testing, bad parameter
(1 row(s) affected)
###########################################################################
will error out in B - should log B and A>>> EXEC ProcedureA 10,'abcd'
(0 row(s) affected)
(1 row(s) affected)
(1 row(s) affected)
(1 row(s) affected)
(1 row(s) affected)
(1 row(s) affected)
@ReturnValue @ErrorMsg
------------ ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
999 ProcedureA: @ParamA1='10', @ParamA2='abcd'; Msg 266, Level 16, State 2, Procedure ProcedureB, Line 0, Transaction count after EXECUTE indicates that a COMMIT or ROLLBACK TRANSACTION statement is missing. Previous count = 1, current count = 0.
(1 row(s) affected)
YourTestTable TestID TestData
------------- ----------- ----------------------------------------------------------------------------------------------------
(0 row(s) affected)
YourLogTable LogID LogDate ProcedureName LogText
------------ ----------- ----------------------- -------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
YourLogTable 38 2009-09-08 15:04:39.680 ProcedureB ProcedureB: @ParamB1='10', @ParamB2='abcd'; Msg 50000, Level 16, State 1, Procedure ProcedureB, Line 31, testing, bad parameter
YourLogTable 39 2009-09-08 15:04:39.680 ProcedureA ProcedureA: @ParamA1='10', @ParamA2='abcd'; Msg 266, Level 16, State 2, Procedure ProcedureB, Line 0, Transaction count after EXECUTE indicates that a COMMIT or ROLLBACK TRANSACTION statement is missing. Previous count = 1, current count = 0.
(2 row(s) affected)
###########################################################################
will error out in C - should log C, B and A>>>> EXEC ProcedureA 100,'123'
(0 row(s) affected)
(2 row(s) affected)
(1 row(s) affected)
(1 row(s) affected)
(1 row(s) affected)
(1 row(s) affected)
(1 row(s) affected)
(1 row(s) affected)
@ReturnValue @ErrorMsg
------------ ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
999 ProcedureA: @ParamA1='100', @ParamA2='123'; Msg 266, Level 16, State 2, Procedure ProcedureB, Line 0, Transaction count after EXECUTE indicates that a COMMIT or ROLLBACK TRANSACTION statement is missing. Previous count = 1, current count = 0.
(1 row(s) affected)
YourTestTable TestID TestData
------------- ----------- ----------------------------------------------------------------------------------------------------
(0 row(s) affected)
YourLogTable LogID LogDate ProcedureName LogText
------------ ----------- ----------------------- -------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------