据此,您可以在 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 中处理嵌套事务,并且仍然记录实际发生的错误?