我有一个带有TRY CATCH语句的存储过程,并且在该TRY CATCH中我正在调用另一个抛出错误的存储过程。抛出并捕获异常但是如果错误在被调用的存储过程中,则不会在ERROR_PROCEDURE()中显示,它将设置为NULL。似乎原因是动态SQL在被调用的存储过程中执行。
ALTER PROC dbo.MyError AS
BEGIN
SET NOCOUNT, XACT_ABORT ON;
BEGIN TRY
BEGIN TRAN
--do stuff here
--SQL CODE
SELECT 'HELLO' AS hello
--then call sproc
EXEC dbo.MyInnerError
COMMIT TRANSACTION
END TRY
BEGIN CATCH
IF (@@TRANCOUNT > 0)
BEGIN
ROLLBACK TRANSACTION
END
SELECT
ERROR_NUMBER() AS ErrorNumber,
ERROR_SEVERITY() AS ErrorSeverity,
ERROR_STATE() AS ErrorState,
ERROR_PROCEDURE() AS ErrorProcedure,
ERROR_LINE() AS ErrorLine,
ERROR_MESSAGE() AS ErrorMessage;
END CATCH
END
GO
ALTER PROC dbo.MyInnerError AS
BEGIN
DECLARE @SQl nvarchar(50) = 'SELECT 1/0 as DYNAMIC_FAIL';
EXEC SP_EXECUTESQL @SQl;
END
EXEC dbo.MyError
GO
我已尝试将sproc嵌套在自己的TRY CATCH中,但这会导致TRANSACTION ROLLBACK问题。
ERROR_PROCEDURE是否为空,因为它超出了范围?有没有办法设置这个?
似乎原因是由于动态SQL在被调用的存储过程中执行。有办法解决这个问题吗?
答案 0 :(得分:1)
根据更新的问题和评论进行修改
ERROR_PROCEDURE()不会返回通过SP_EXECUTESQL执行的SQL的过程名称。从逻辑上讲,如果确实如此,它将返回' SP_EXECUTESQL' :)。请参阅此Connect条目" TRY/CATCH: ERROR_PROCEDURE() does not report name of procedure if error occured in dynamic SQL",特别是Microsoft的回复中的这句话;
由于没有与ad hoc SQL关联的名称,ERROR_PROCEDURE 对于从广告执行级别引发的错误,将返回NULL 特别的SQL。
我敲了一个非常快速的测试,它适用于我(SQL Server 2012);
CREATE PROC dbo.MyError AS
BEGIN
SET NOCOUNT, XACT_ABORT ON;
BEGIN TRY
BEGIN TRAN
--do stuff here
--SQL CODE
--then call sproc
EXEC dbo.MyInnerError
COMMIT TRANSACTION
END TRY
BEGIN CATCH
IF (@@TRANCOUNT > 0)
BEGIN
ROLLBACK TRANSACTION
END
SELECT
ERROR_NUMBER() AS ErrorNumber,
ERROR_SEVERITY() AS ErrorSeverity,
ERROR_STATE() AS ErrorState,
ERROR_PROCEDURE() AS ErrorProcedure,
ERROR_LINE() AS ErrorLine,
ERROR_MESSAGE() AS ErrorMessage;
END CATCH
END
GO
CREATE PROC dbo.MyInnerError AS
BEGIN
;THROW 51000, 'This is my only error.', 1;
END
GO
EXEC dbo.MyError
GO
结果是;
ErrorNumber ErrorSeverity ErrorState ErrorProcedure ErrorLine ErrorMessage
----------- ------------- ----------- ---------------- ----------- ------------------------
51000 16 1 MyInnerError 4 This is my only error.
答案 1 :(得分:0)
问题是对MyInnerError
的子进程调用没有失败; <{1}}内的来电失败,但MyInnerError
完全成功。
MyInnerError
已成功完成,因为您没有通过MyInnerError
/ TRY
结构捕获错误并报告类似于外部过程中的失败。
那,来自动态SQL的错误自然不会设置CATCH
。
所有的proc都应该具有ERROR_PROCEDURE()
/ TRY
结构,CATCH
块应该使用RAISERROR或THROW(取决于您使用的SQL Server版本),以便你可以将错误冒充到调用范围。
CATCH
DECLARE @InNestedTransaction BIT = 0;
BEGIN TRY
IF (@@TRANCOUNT > 0)
BEGIN
SET @InNestedTransaction = 1;
END;
ELSE
BEGIN
BEGIN TRAN;
END;
... one or more SQL statements ...
COMMIT;
END TRY
BEGIN CATCH
IF (@InNestedTransaction = 0)
BEGIN
ROLLBACK;
END;
IF (ERROR_PROCEDURE() IS NULL)
BEGIN
DECLARE @ErrMessage NVARCHAR(4000) = ERROR_MESSAGE(),
@ErrState TINYINT = ERROR_STATE(),
@ErrSeverity TINYINT = ERROR_SEVERITY();
RAISERROR(@ErrMessage, @ErrSeverity, @ErrState);
RETURN;
END;
;THROW; -- introduced in SQL Server 2012
---- If using SQL Server 2008, replace the above (from "IF" through "THROW")
---- with the following.
-- DECLARE @ErrMessage NVARCHAR(4000) = ERROR_MESSAGE();
-- RAISERROR(@ErrMessage, 16, 1);
-- RETURN;
END CATCH;
块用于捕获这样的情况,其中没有proc生成错误。单独调用IF (ERROR_PROCEDURE() IS NULL)
会使当前错误信息冒泡,在这种情况下;THROW;
为NULL
。
如果您想自己测试,只需运行以下SQL,它创建3个存储过程,所有这些过程都使用上面显示的结构。最内层的过程(ErrorTest1)使用ERROR_PROCEDURE()
作为查询来调用sp_executesql
。 “除以零”错误被TRY / CATCH捕获。 SELECT 1/0;
函数返回ERROR_PROCEDURE()
,因为它是生成错误的动态SQL。因此,调用NULL
(技术上调用RAISERROR
也可以),以向调用进程指示当前proc生成错误。
测试设置:
;THROW 50505, @ErrMessage, @ErrState;
运行测试:
IF (OBJECT_ID(N'ErrorTest3') IS NOT NULL)
BEGIN
DROP PROCEDURE ErrorTest3;
END;
IF (OBJECT_ID(N'ErrorTest2') IS NOT NULL)
BEGIN
DROP PROCEDURE ErrorTest2;
END;
IF (OBJECT_ID(N'ErrorTest1') IS NOT NULL)
BEGIN
DROP PROCEDURE ErrorTest1;
END;
GO
CREATE PROCEDURE dbo.ErrorTest1
AS
SET NOCOUNT ON;
DECLARE @InNestedTransaction BIT = 0;
BEGIN TRY
IF (@@TRANCOUNT > 0)
BEGIN
SET @InNestedTransaction = 1;
END;
ELSE
BEGIN
BEGIN TRAN;
END;
SELECT '1a';
EXEC sp_executesql N'SELECT 1/0 AS [ForceError];';
SELECT '1b';
COMMIT;
END TRY
BEGIN CATCH
IF (@InNestedTransaction = 0)
BEGIN
ROLLBACK;
END;
IF (ERROR_PROCEDURE() IS NULL)
BEGIN
DECLARE @ErrMessage NVARCHAR(4000) = ERROR_MESSAGE(),
@ErrState TINYINT = ERROR_STATE(),
@ErrSeverity TINYINT = ERROR_SEVERITY();
RAISERROR(@ErrMessage, @ErrSeverity, @ErrState);
RETURN;
END;
;THROW; -- introduced in SQL Server 2012
END CATCH;
GO
CREATE PROCEDURE dbo.ErrorTest2
AS
SET NOCOUNT ON;
DECLARE @InNestedTransaction BIT = 0;
BEGIN TRY
IF (@@TRANCOUNT > 0)
BEGIN
SET @InNestedTransaction = 1;
END;
ELSE
BEGIN
BEGIN TRAN;
END;
SELECT '2a';
EXEC dbo.ErrorTest1;
SELECT '2b';
COMMIT;
END TRY
BEGIN CATCH
IF (@InNestedTransaction = 0)
BEGIN
ROLLBACK;
END;
IF (ERROR_PROCEDURE() IS NULL)
BEGIN
DECLARE @ErrMessage NVARCHAR(4000) = ERROR_MESSAGE(),
@ErrState TINYINT = ERROR_STATE(),
@ErrSeverity TINYINT = ERROR_SEVERITY();
RAISERROR(@ErrMessage, @ErrSeverity, @ErrState);
RETURN;
END;
;THROW; -- introduced in SQL Server 2012
END CATCH;
GO
CREATE PROCEDURE dbo.ErrorTest3
AS
SET NOCOUNT ON;
DECLARE @InNestedTransaction BIT = 0;
BEGIN TRY
IF (@@TRANCOUNT > 0)
BEGIN
SET @InNestedTransaction = 1;
END;
ELSE
BEGIN
BEGIN TRAN;
END;
SELECT '3a';
EXEC dbo.ErrorTest2;
SELECT '3b';
COMMIT;
END TRY
BEGIN CATCH
IF (@InNestedTransaction = 0)
BEGIN
ROLLBACK;
END;
SELECT ERROR_PROCEDURE() AS [ErrorProcedure],
ERROR_STATE() AS [ErrorState],
ERROR_SEVERITY() AS [ErrorSeverity];
IF (ERROR_PROCEDURE() IS NULL)
BEGIN
DECLARE @ErrMessage NVARCHAR(4000) = ERROR_MESSAGE(),
@ErrState TINYINT = ERROR_STATE(),
@ErrSeverity TINYINT = ERROR_SEVERITY();
RAISERROR(@ErrMessage, @ErrSeverity, @ErrState);
RETURN;
END;
;THROW; -- introduced in SQL Server 2012
END CATCH;
GO
<强>返回:强>
5个结果集:
EXEC dbo.ErrorTest3;
在“消息”标签中:
Msg 50000,Level 16,State 1,Procedure ErrorTest1,Line 36
除以遇到的零错误。