這裏是回滾嵌套過程記錄的例子...
這將創建兩個測試表和三個過程,可以嵌套的方式與交易被稱爲:
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(''''[email protected]+'''','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 @[email protected]/@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 @[email protected]+'; '
+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(''''[email protected]+'''','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 @[email protected]/@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 @[email protected]+'; '
+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 @[email protected]/@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 @[email protected]+'; '
+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
這將運行四個測試用例上面的程序:
- 了失效,YourTestTable中沒有任何數據,YourLogTable顯示日誌的信息
信息
- 無法在B,YourTestTable中沒有任何數據,YourLogTable顯示日誌信息爲B和A
- 無法在C,YourTestTable中沒有任何數據,YourLogTable顯示日誌信息爲C,B和A
所有工作,YourTestTable中有數據,YourLogTable中有
代碼在這裏沒有數據:
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
------------ ----------- ----------------------- -------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
從長遠來看,混合的try-catch邏輯*和*返回錯誤代碼不會起作用。保證有人會忘記檢查一個程序返回代碼,並依靠存在已經回滾的交易來繼續前進!無論單獨依靠異常還是通過使用保存點正確處理XACT_STATE的所有3個狀態,從而允許調用者決定是否在錯誤情況下回滾或在其他路徑上繼續,都可以說是更好。許多錯誤是可以恢復的。請參閱http://rusanu.com/2009/06/11/exception-handling-and-nested-transactions/ – 2009-09-22 22:10:56
謝謝你這樣一個徹底的例子! – javacavaj 2009-09-23 02:13:24
@Remus Rusanu,我們從不使用保存點並總是回滾,所以調用者沒有多少選項,因爲事務計數不匹配,並且會自動進入catch。 – 2009-09-23 13:17:15