【发布时间】:2015-07-31 17:48:16
【问题描述】:
根据this,你可以在catch块中有一个状态,除非你先回滚,否则你不能做任何写操作。
当您尝试处理嵌套事务并进行错误记录时,这是一个问题。在下面的示例中,嵌套过程中的异常会丢失并且不会记录任何内容。
IF OBJECT_ID(N'dbo.ErrorLog', N'U') IS NOT NULL
DROP TABLE dbo.ErrorLog;
GO
CREATE TABLE dbo.ErrorLog (Error NVARCHAR(4000));
GO
IF OBJECT_ID(N'tempdb..#Caller') IS NOT NULL
BEGIN
DROP PROC #Caller;
END;
GO
CREATE PROCEDURE #Caller
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT ON;
DECLARE @transCount TINYINT = @@TRANCOUNT,
@returnCode INT,
@errorMessage NVARCHAR(4000),
@errorNumber INT;
BEGIN TRY
IF (@transCount = 0)
BEGIN
BEGIN TRAN;
END;
EXEC @returnCode = #Called;
IF (@returnCode <> 0)
BEGIN
RAISERROR(N'Error in Called. Caller returned an error', 16, -1);
END;
IF (@transCount = 0)
BEGIN
COMMIT TRAN;
END;
END TRY
BEGIN CATCH
IF ((@transCount = 0) AND (XACT_STATE() <> 0))
BEGIN
ROLLBACK TRAN;
END;
SELECT @errorMessage = ERROR_MESSAGE(),
@errorNumber = ERROR_NUMBER();
INSERT dbo.ErrorLog(Error) VALUES(@errorMessage); --only this logging happens
RAISERROR(N'Error in Caller.', 16, -1);
RETURN @errorNumber;
END CATCH;
RETURN;
END;
GO
IF OBJECT_ID(N'tempdb..#Called') IS NOT NULL
BEGIN
DROP PROC #Called;
END;
GO
CREATE PROC #Called
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT ON;
DECLARE @transCount TINYINT = @@TRANCOUNT,
@errorMessage NVARCHAR(4000),
@errorNumber INT;
BEGIN TRY
IF (@transCount = 0) --doesn't start tran, already in one
BEGIN
BEGIN TRAN;
END;
SELECT 1/0; --generate an error; this exception gets lost
IF (@transCount = 0)
BEGIN
COMMIT TRAN;
END;
END TRY
BEGIN CATCH
IF ((@transCount = 0) AND (XACT_STATE() <> 0)) --cannot rollback here because this didn't start the transaction
BEGIN
ROLLBACK TRAN;
END;
SELECT @errorMessage = ERROR_MESSAGE(),
@errorNumber = ERROR_NUMBER();
INSERT dbo.ErrorLog(Error) VALUES(@errorMessage); --doesn't happen because of uncommitable transaction; raises exception, caught in CATCH block of Caller
RAISERROR(N'Error in Called.', 16, -1); --this doesn't happen
RETURN @errorNumber; --nothing returned
END CATCH;
RETURN;
END
GO
EXEC dbo.#Caller;
GO
SELECT * FROM dbo.ErrorLog;
GO
记录的单个错误只是不可提交的事务异常。有没有办法在 TRY..CATCH 中处理嵌套事务,并且仍然记录实际发生的错误?
【问题讨论】:
-
我知道的唯一方法是使用表变量,因为它们不受回滚的影响,但如果您的逻辑很复杂,它可能会变得非常混乱。
-
不确定这是否可行,嘿。错误日志应该是一个持久表。表变量将在执行后被清理。我还认为表变量插入在这里也不起作用,因为它是一个写操作,并且在不可提交状态下是不允许的。
-
写入表变量必须在catch之外,或者至少我假设,以及在最终回滚后从表变量写入错误日志表。生存过程边界可能需要临时。表或其他东西来携带数据。当然,如果错误只是一行,输出参数应该可以工作。
-
我所做的,而且似乎有帮助,就是将我的错误日志记录在 CATCH 中,并在 TRY..CATCH 中调用。如果日志记录失败,我只需通过原始异常传递。一个 RAISERROR 到执行日志记录的调用程序过程。
标签: sql-server tsql stored-procedures try-catch raiserror