SQL SERVER 2008R2使用RAISERROR的嵌套事务

jab*_*own 5 sql sql-server sql-server-2008 sql-server-2008-r2

我们正在经历从DB2切换到SQL Server 2008R2的过程,我对TSQL有点不熟悉.任何有助于更好地了解正在发生的事情的帮助都会很好.我们创建了一个名为RethrowError的过程:

CREATE PROCEDURE RethrowError 
AS
BEGIN
    -- Return if there is no error information to retrieve.
    IF ERROR_NUMBER() IS NULL
        RETURN;
 PRINT 'yo error'; 

    DECLARE 
        @ErrorMessage    NVARCHAR(4000),
        @ErrorNumber     INT,
        @ErrorSeverity   INT,
        @ErrorState      INT,
        @ErrorLine       INT,
        @ErrorProcedure  NVARCHAR(200);

    -- Assign variables to error-handling functions that 
    -- capture information for RAISERROR.
    SELECT 
        @ErrorNumber     = ERROR_NUMBER(),
        @ErrorSeverity   = ERROR_SEVERITY(),
        @ErrorState      = ERROR_STATE(),
        @ErrorLine       = ERROR_LINE(),
        @ErrorProcedure  = ISNULL(ERROR_PROCEDURE(), '-');

    -- Build the message string that will contain original
    -- error information.
    SELECT @ErrorMessage = N'Error %d, Level %d, State %d, Procedure %s, Line %d, ' + 
                           'Message: '+ ERROR_MESSAGE();
 PRINT 'yo doin something'; 

    -- Raise an error: msg_str parameter of RAISERROR will contain
    -- the original error information.
    RAISERROR 
        (
        @ErrorMessage, 
        @ErrorSeverity, 
        1,               
        @ErrorNumber,    -- parameter: original error number.
        @ErrorSeverity,  -- parameter: original error severity.
        @ErrorState,     -- parameter: original error state.
        @ErrorProcedure, -- parameter: original error procedure name.
        @ErrorLine       -- parameter: original error line number.
        );
 PRINT 'yo end'; 

   RETURN;
END
GO
Run Code Online (Sandbox Code Playgroud)

我们创建该过程的原因纯粹是为了在将来扩展错误而无需触及所有过程. 我添加了一些PRINT行用于调试目的.

我的主要问题是我们有程序A,如果失败,它会执行RethrowError,我会看到消息

yo error
yo doin something
yo end
Run Code Online (Sandbox Code Playgroud)

正如所料.

CREATE PROCEDURE dbo.A
AS
BEGIN
   SET NOCOUNT ON;

   DECLARE & SET VARIABLES;
BEGIN TRY
   BEGIN TRANSACTION MaintainTarget

   DO SOME STUFF
END TRY
BEGIN CATCH
   EXEC RethrowError;

    IF (XACT_STATE()) = -1
    BEGIN
        PRINT
            N'The transaction is in an uncommittable state. ' +
            'Rolling back transaction.'
        ROLLBACK TRANSACTION;
    END;


    IF (XACT_STATE()) = 1
    BEGIN

        PRINT
            N'The transaction is committable. ' +
            'Rolling back transaction.'
        ROLLBACK TRANSACTION;
    END;

     RETURN -101;
END CATCH;
RETURN;
END

GO
Run Code Online (Sandbox Code Playgroud)

但是,我们创建了一个执行许多过程的过程,当嵌套过程(即过程B调用过程A)失败时,我看到的唯一消息是

yo error
yo doin something
Run Code Online (Sandbox Code Playgroud)

我不太明白为什么最后一条消息不再出现.

程序B类似于程序A,但在捕获方面略有不同.

CREATE PROCEDURE dbo.B
AS
BEGIN
   SET NOCOUNT ON;

   DECLARE & SET VARIABLES;
BEGIN TRY    
   DO SOME STUFF
END TRY
BEGIN CATCH
 COMMIT;

 RETURN -101;
END CATCH;
RETURN;
END
Run Code Online (Sandbox Code Playgroud)

任何帮助,以更好地了解正在发生的事情将不胜感激.

Ada*_*ski 3

我允许自己编辑你的代码来模仿行为,但保持简单(实际上是你的工作;)。

您的 procA 工作正常,因为在 procA 的 CATCH 块内调用 RethrowError 过程并且所有内容都会执行。但在第二种情况下,这一切仍然发生在 procB 的 TRY 块内!因此,在调用 RethrowError 中的 RAISERROR 后,procB 的 CATCH 部分会立即触发。

这个简单的示例演示了 TRY-CATCH 的这种行为:

begin try
    select 1/0
    print 'doesnt show - div error'
end try
begin catch
    print 'oops'
    select 1/0
    print 'this one shows because its in CATCH!'
end catch
Run Code Online (Sandbox Code Playgroud)

这是您的简化代码:

-- "proc B" start
begin try
    -- "proc A" start (works fine alone)
    begin try
        begin tran
        select 1/0  --error
    end try
    begin catch
        print 'yo error';
        RAISERROR ('RE from RethrowError', 16, 1)   --comment this out and see what happens
        print 'yo end';

        IF (XACT_STATE())=-1 or (XACT_STATE())=1
        BEGIN
            PRINT N'Rolling back transaction.'
            ROLLBACK TRANSACTION;
        end
    end catch   -- "proc A" ends
end try
begin catch
    select error_message(), error_severity(), error_state() --
    print 'outer catch';
    commit;
end catch;
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助。