不可提交的事务可防止嵌套事务中的错误记录

时间:2015-05-19 18:31:30

标签: sql-server tsql stored-procedures try-catch raiserror

根据this,你可以在catch块中有一个状态,除非你先回滚,否则你不能进行任何写操作。

当您尝试处理嵌套事务并执行错误记录时,这是一个问题。在以下示例中,嵌套过程中的异常会丢失,并且不会记录任何内容。

<form method="post" action="CreateServlet" name = "create">

Change Number(s):<br>
<input type="text" name="change" id="change">
<p></p>
<input type="submit" value="Download" onClick="return validateForm()">

</form>
<p></p>
<button  id="send" name="send">Display</button>

记录的单个错误只是不可承受的事务异常。有没有办法在TRY..CATCH中处理嵌套事务,还是记录实际发生的错误?

1 个答案:

答案 0 :(得分:0)

这种方法帮助我实现了我所需要的。基本上,在注定的tran(XACT_STATE()= -1)的情况下,整个事务被回滚以允许在两个级别上进行错误记录,同时在执行结束时仅引发一个异常。

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)) OR (XACT_STATE() = -1))
    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)) OR (XACT_STATE() = -1)) --rollback even though didn't start this tran because its doomed
    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


DECLARE @returnCode INT;
EXEC @returnCode = dbo.#Caller; --one exception raised
SELECT @returnCode AS 'returnCode'; --error code 50000 returned

SELECT * FROM dbo.ErrorLog; --both errors logged incl original exception