Sql server 如何获取在存储过程中执行的存储过程的自定义错误?

Sql server 如何获取在存储过程中执行的存储过程的自定义错误?,sql-server,stored-procedures,Sql Server,Stored Procedures,我目前正在研究如何处理SQL server中嵌套存储过程的异常 不幸的是,遇到了一个场景,我想在存储过程[1]内部执行的存储过程[2]上输出自定义错误 以下是一个例子: /* create the master-table */ CREATE TABLE master_table ( pk_id INT IDENTITY(5,5), access_num INT UNIQUE NOT NULL, first_name NVARCHAR(MAX) PRIMA

我目前正在研究如何处理SQL server中嵌套存储过程的异常

不幸的是,遇到了一个场景,我想在存储过程[1]内部执行的存储过程[2]上输出自定义错误

以下是一个例子:

/*
    create the master-table
*/
CREATE TABLE master_table
(
    pk_id INT IDENTITY(5,5),
    access_num INT UNIQUE NOT NULL,
    first_name NVARCHAR(MAX)
    PRIMARY KEY (pk_id)
)
GO

/*
    create the child-table
*/
CREATE TABLE child_table
(
    pk_id INT IDENTITY(1,1),
    val INT UNIQUE,
    msgs NVARCHAR(MAX) NOT NULL
    PRIMARY KEY (pk_id)
)
GO


/*
    create the master procedure
*/
CREATE or ALTER PROC uspInsertIntoMaster
    @AccessNum INT,
    @FirstName NVARCHAR(MAX),
    @Message NVARCHAR(MAX)
AS
BEGIN
    DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS;
    SET XACT_ABORT, NOCOUNT ON;

    BEGIN TRY
        BEGIN TRAN
            INSERT INTO master_table (access_num, first_name)
            VALUES (@AccessNum, @FirstName)

            EXEC uspInsertIntoChild 4, @Message;

        IF (@@TRANCOUNT = 1)
            COMMIT
    END TRY

    BEGIN CATCH
        IF @@TRANCOUNT > 0 ROLLBACK;

        THROW;

        IF (XACT_STATE()) = -1
                BEGIN
                    ROLLBACK TRANSACTION;
                END;

        IF (XACT_STATE()) = 1
            BEGIN
                COMMIT TRANSACTION;
            END;
    END CATCH
END
GO


/*
    create the child procedure
*/
CREATE or ALTER PROC uspInsertIntoChild
    @Value INT,
    @Message NVARCHAR(MAX)
AS
    DECLARE @ErrorMessage NVARCHAR(2048);
BEGIN
    DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS;
    SET XACT_ABORT, NOCOUNT ON;

    BEGIN TRY
        IF EXISTS(SELECT 1 FROM child_table WHERE val = @Value)
            BEGIN
                SET @ErrorMessage = FORMATMESSAGE(50010, @Value);
                THROW 50010, @ErrorMessage, 1;
            END;

        ELSE
            BEGIN TRAN 
                INSERT INTO child_table (val, msgs) VALUES (@Value, @Message)

            IF (@@TRANCOUNT > 0)
                COMMIT
    END TRY

    BEGIN CATCH
        IF @@TRANCOUNT > 0 ROLLBACK;

        THROW;

        IF (XACT_STATE()) = -1
                BEGIN
                    ROLLBACK TRANSACTION;
                END;

        IF (XACT_STATE()) = 1
            BEGIN
                COMMIT TRANSACTION;
            END;
    END CATCH
END
GO

自定义错误消息

EXEC sys.sp_addmessage 
    @msgnum = 50010, 
    @severity = 16, 
    @msgtext = N'Invalid Input. Error occurred at procedure uspInsertIntoChild',
    @lang = 'us_english';   
GO
EXEC sys.sp_addmessage 
    @msgnum = 50010, 
    @severity = 16, 
    @msgtext =
    N'Invalid Input %i. Error occured at procedure uspInsertIntoChild', /*Mobile number [%s] is invalid. Input a valid number*/
    @lang = 'us_english';   
GO
如果我尝试执行下面的代码。我希望它在第二次执行时失败,并抛出上面的自定义错误

EXEC uspInsertIntoMaster 4, N'miKeL', N'Get Well Soon'
EXEC uspInsertIntoMaster 5, N'miKeL', N'Get Well Soon'
但不幸的是,我没有自定义错误

Msg 50010,16级,状态1,程序uspInsertIntoChild,第14行 [批处理起始行105]错误:50010,严重性:16,状态:1。 (参数:)。错误以简洁模式打印,因为存在错误 在格式化期间。跳过跟踪、ETW、通知等


编辑(@Charlieface) 首先,我先从
master proc
中删除了
TRY/CATCH
块,然后删除了这两个块,但仍然出现相同的错误。我还按照@M.Ali的建议,删除了没有INFOMSGS的DBCC dropcleanbuffer

新建
主程序

CREATE or ALTER PROC uspInsertIntoMaster
    @AccessNum INT,
    @FirstName NVARCHAR(30),
    @Message NVARCHAR(MAX)
AS
BEGIN
    SET XACT_ABORT, NOCOUNT ON;
    
    BEGIN TRANSACTION
        INSERT INTO master_table (access_num, first_name)
        VALUES (@AccessNum, @FirstName)

        EXEC uspInsertIntoChild 4, @Message;

    IF (@@TRANCOUNT = 1)
        COMMIT TRANSACTION
END
GO
CREATE or ALTER PROC uspInsertIntoChild
    @Value INT,
    @Message NVARCHAR(MAX)
AS
    DECLARE @ErrorMessage NVARCHAR(MAX);
BEGIN
    
    SET XACT_ABORT, NOCOUNT ON;
    IF EXISTS(SELECT 1 FROM child_table WHERE val = @Value)
        BEGIN
            SET @ErrorMessage = FORMATMESSAGE(50010, @Value);
            -- I want to thow
            THROW 50010, @ErrorMessage, 1;
        END;

    ELSE
        BEGIN TRANSACTION
            INSERT INTO child_table (val, msgs) VALUES (@Value, @Message)

        IF (@@TRANCOUNT > 0)
            COMMIT TRANSACTION
END
GO
新建
子进程

CREATE or ALTER PROC uspInsertIntoMaster
    @AccessNum INT,
    @FirstName NVARCHAR(30),
    @Message NVARCHAR(MAX)
AS
BEGIN
    SET XACT_ABORT, NOCOUNT ON;
    
    BEGIN TRANSACTION
        INSERT INTO master_table (access_num, first_name)
        VALUES (@AccessNum, @FirstName)

        EXEC uspInsertIntoChild 4, @Message;

    IF (@@TRANCOUNT = 1)
        COMMIT TRANSACTION
END
GO
CREATE or ALTER PROC uspInsertIntoChild
    @Value INT,
    @Message NVARCHAR(MAX)
AS
    DECLARE @ErrorMessage NVARCHAR(MAX);
BEGIN
    
    SET XACT_ABORT, NOCOUNT ON;
    IF EXISTS(SELECT 1 FROM child_table WHERE val = @Value)
        BEGIN
            SET @ErrorMessage = FORMATMESSAGE(50010, @Value);
            -- I want to thow
            THROW 50010, @ErrorMessage, 1;
        END;

    ELSE
        BEGIN TRANSACTION
            INSERT INTO child_table (val, msgs) VALUES (@Value, @Message)

        IF (@@TRANCOUNT > 0)
            COMMIT TRANSACTION
END
GO
编辑2 我真傻。我编辑了格式化的消息。但从未想过还要编辑表示字符串的控制字符
%s
。我试图向错误消息传递一个整数参数,因此它应该是
%I

最终
格式化
错误消息

EXEC sys.sp_addmessage 
    @msgnum = 50010, 
    @severity = 16, 
    @msgtext = N'Invalid Input. Error occurred at procedure uspInsertIntoChild',
    @lang = 'us_english';   
GO
EXEC sys.sp_addmessage 
    @msgnum = 50010, 
    @severity = 16, 
    @msgtext =
    N'Invalid Input %i. Error occured at procedure uspInsertIntoChild', /*Mobile number [%s] is invalid. Input a valid number*/
    @lang = 'us_english';   
GO

您正在使用的消息没有任何格式参数,因此
FORMATMESSAGE
引发错误。

你需要这样的东西:

EXEC sys.sp\u addmessage
@msgnum=50010,
@严重性=16,
@msgtext=N'输入%s无效。过程uspInsertIntoChild'发生错误,
@lang=‘美国英语’;

顺便说一句,如果您在上使用
XACT\u ABORT,则无需捕获和回滚。仅当您使用自定义日志记录时才有必要,在本例中,您不需要

此外,由于其他原因,您当前使用的代码不起作用:

  • CATCH
    中,您通常希望
    ROLLBACK
    never
    COMMIT
  • 如果
    XACT\u STATE=-1
    回滚将失败
  • 抛出后不执行代码无论如何

所以我想说删除整个
TRY/CATCH
,保留
XACT\u ABORT ON
并记录消息,就像使用
sp\u addmessage

一样,为什么不使用抛出自定义错误消息呢?文档中提供了大量的示例来向您展示如何做到这一点:O只是出于兴趣,为什么您有
DBCC DROPCLEANBUFFERS而没有任何infomsg在您的过程定义中?我希望您理解此命令的含义,并意识到它可能对您的应用程序和服务器速度造成的危害。此外,错误消息的长度不能超过10亿个字符,它们的上限为2048个字符。您的提交事务位于
Catch Block
中,它应该位于
Try Block
中。请查看此答案,它将帮助您了解Try和Catch Block以及raiserror/throw函数的正确用法
first\u name NVARCHAR(MAX)
。真正地谁的名字这么长
CREATE or ALTER PROC uspInsertIntoMaster
    @AccessNum INT,
    @FirstName NVARCHAR(30),
    @Message NVARCHAR(MAX)
AS
BEGIN
    SET XACT_ABORT, NOCOUNT ON;
    
    BEGIN TRANSACTION
        INSERT INTO master_table (access_num, first_name)
        VALUES (@AccessNum, @FirstName)

        EXEC uspInsertIntoChild 4, @Message;

    IF (@@TRANCOUNT = 1)
        COMMIT TRANSACTION
END
GO
CREATE or ALTER PROC uspInsertIntoChild
    @Value INT,
    @Message NVARCHAR(MAX)
AS
    DECLARE @ErrorMessage NVARCHAR(MAX);
BEGIN
    
    SET XACT_ABORT, NOCOUNT ON;
    IF EXISTS(SELECT 1 FROM child_table WHERE val = @Value)
        BEGIN
            SET @ErrorMessage = FORMATMESSAGE(50010, @Value);
            -- I want to thow
            THROW 50010, @ErrorMessage, 1;
        END;

    ELSE
        BEGIN TRANSACTION
            INSERT INTO child_table (val, msgs) VALUES (@Value, @Message)

        IF (@@TRANCOUNT > 0)
            COMMIT TRANSACTION
END
GO