在我的VBA应用程序中捕获SQL错误时遇到了一些问题,我重新设计了我的存储过程,以便在发生错误时,返回值是错误代码,输出变量包含错误消息。我不会在我的catch块中重新抛出错误。由于缺乏一个更好的术语,我会称之为“优雅退出”。它使客户端的操作变得更容易,但是当嵌套存储过程触发的触发器回滚事务时,我遇到了一个问题。
采用以下示例。 TEST_INNER_PROC
以@@TRANCOUNT
为1开头,执行插入触发触发器,回滚事务,当TEST_INNER_PROC
退出时抛出错误
266:EXECUTE之后的事务计数表示BEGIN和COMMIT语句的数量不匹配
通常情况下,我会将这两个程序的模式相同;我在这里简化了它们。内部过程不会尝试启动事务(它不会产生任何影响),并且外部过程会重新抛出错误,这样我就可以看到打印的错误信息。通常,我会通过返回码和@ERR_MSG
输出变量将错误代码返回给客户端。
我喜欢@ gbn的模式:Nested stored procedures containing TRY CATCH ROLLBACK pattern?但是,如果在触发器中发生回滚,它似乎不适合我的“优雅退出”。我也不确定Rusanu's pattern 也适应它。
CREATE TABLE TEST (
COL1 INT
)
GO
CREATE TRIGGER TEST_TRIGGER
ON TEST FOR INSERT
AS
BEGIN TRY
THROW 50001, 'TEST Trigger produced an error.', 1
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0 AND XACT_STATE()<>0
ROLLBACK TRAN;
THROW
END CATCH
GO
CREATE PROC TEST_INNER_PROC
AS
SET NOCOUNT, XACT_ABORT ON
DECLARE @RTN INT = 0
BEGIN TRY
INSERT TEST (COL1) VALUES (1)
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0 AND XACT_STATE()<>0
ROLLBACK TRAN
SET @RTN = ERROR_NUMBER();
--THROW
END CATCH
RETURN @RTN
GO
CREATE PROC TEST_OUTER_PROC
AS
SET NOCOUNT, XACT_ABORT ON
DECLARE @RTN INT = 0
BEGIN TRY
BEGIN TRAN
EXEC @RTN = TEST_INNER_PROC
IF @RTN <> 0 THROW 50000, 'Execution of TEST_INNER_PROC produced an error.', 1
COMMIT TRAN
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0 AND XACT_STATE()<>0
ROLLBACK TRAN;
THROW
END CATCH
GO
EXEC TEST_OUTER_PROC
GO
DROP TABLE TEST
DROP PROC TEST_OUTER_PROC
DROP PROC TEST_INNER_PROC
GO
上面的代码导致:
Msg 266,Level 16,State 2,Procedure TEST_INNER_PROC,Line 63
EXECUTE之后的事务计数表示BEGIN的数量不匹配 和COMMIT语句。先前的计数= 1,当前计数= 0。
但如果您取消注释TEST_INNER_PROC中的“THROW”语句,它会抛出:
Msg 50001,Level 16,State 1,Procedure TEST_TRIGGER,第69行 TEST触发器产生错误。
这是我要在TEST_OUTER_PROC
中处理的错误。
是否可以使用“正常退出”的存储过程,将错误代码和错误消息作为变量返回,并避免BEGIN
和COMMIT
语句的不匹配数量?