从存储过程返回错误消息

Posted

技术标签:

【中文标题】从存储过程返回错误消息【英文标题】:Return error message from stored procedure 【发布时间】:2016-01-21 12:38:23 【问题描述】:

问题应该很简单,但我不知道答案,也不知道为什么我的存储过程不工作。

CREATE PROCEDURE spTest_Delete
@ID int
AS
    begin tran
        declare @err int
        declare @errMesage nvarchar(max)
        set @errMesage = ''
        set @err = 0

        delete from Test
        where ID = @ID

        set @err = @@ERROR
        set @errMesage = ERROR_MESSAGE()

        if @err = 0 
           commit tran
        else 
        begin
            RAISERROR(N'Could not delete !Error nr: %d. Message: %s', 1, 16, @err, @errMesage)
            rollback tran
        end

此过程运行正常,但在 delete 语句上存在 FK 约束的情况下,它会遇到错误(这很好),我想捕获该错误。

消息 547,级别 16,状态 0,过程 spTest_Delete,第 12 行 DELETE 语句与 REFERENCE 约束冲突 “FK_TEstFK_Test”。数据库“Test”中发生冲突,表 “dbo.Test”,“ID”列。该语句已终止。

无法删除! 错误编号:547。消息:(空)消息 50000,级别 1,状态 16

我的消息变量总是为 null,即使 delete 语句引发错误。

【问题讨论】:

【参考方案1】:

尝试使用 TRY CATCH 并像这样捕获您的错误:

BEGIN TRY
   delete from Test
    where  ID = @ID
END TRY
BEGIN CATCH
    SET @ErrorMessage  = ERROR_MESSAGE()
    SET @ErrorSeverity = ERROR_SEVERITY()
    SET @ErrorState    = ERROR_STATE()
    RAISERROR(@ErrorMessage, @ErrorSeverity, @ErrorState)
    BREAK
END CATCH

【讨论】:

Try catch 成功了,但我还是有点困惑,为什么没有 try catch 就不行。 @CiucaS:- 通常建议使用 TRY..CATCH 来捕获错误。你可以尝试使用if ISNULL(@err,0) = 0 .net端如何捕获ErrorMessage? 我看到RaiseError 将返回(通过.NET 异常)它的msg_str 参数如果调用者是.NET 应用程序,但它如何返回它,如果调用者是另一个存储过程? Error_Message(以及所有 Error_* 函数)在 Exec 之后立即返回 Null,该存储过程在它之前调用 RaiseError return-ed 并且它也不会触发 Catch在调用存储过程中阻塞。 @Tom 我很想知道你的评论的答案【参考方案2】:

您可能希望开始在您的程序中使用TRY..CATCH 块

为 Transact-SQL 实现错误处理,类似于 Microsoft Visual C# 和 Microsoft Visual C++ 中的异常处理 语言。一组 Transact-SQL 语句可以包含在 TRY 中 堵塞。如果 TRY 块中发生错误,则将控制权传递给 包含在 CATCH 块中的另一组语句。

所以你的程序可以重写为:

CREATE PROCEDURE spTest_Delete @ID INT
AS
BEGIN
    SET NOCOUNT ON;
    BEGIN TRY
        BEGIN TRANSACTION
            DELETE
            FROM Test
            WHERE ID = @ID;
        COMMIT TRANSACTION
    END TRY
    BEGIN CATCH
        IF @@TRANCOUNT > 0
            ROLLBACK TRANSACTION
        SELECT ERROR_NUMBER(), ERROR_MESSAGE();
    END CATCH
END

另外,请注意您是作为单个删除语句运行的。这意味着它不需要包含在事务中。 This 问题解释了原因。

你的代码变成这样:

CREATE PROCEDURE spTest_Delete @ID INT
AS
BEGIN
    SET NOCOUNT ON;
    BEGIN TRY
        DELETE
        FROM Test
        WHERE ID = @ID;
    END TRY
    BEGIN CATCH
        SELECT ERROR_NUMBER(), ERROR_MESSAGE();
    END CATCH
END

现在为什么你的@errMessage 总是为NULL?因为ERROR_MESSAGE() 仅在捕获块中有效。写在documentation:

返回导致 CATCH 块的错误的消息文本 TRY…CATCH 构造被运行。

Using TRY..CATCH in Transact-SQL 告诉这个:

使用这些函数从任何地方检索错误信息 在 TRY…CATCH 构造的 CATCH 块的范围内。 错误 如果在 CATCH 范围之外调用,函数将返回 NULL 阻止

【讨论】:

以上是关于从存储过程返回错误消息的主要内容,如果未能解决你的问题,请参考以下文章

Oracle存储过程OUT CLOB返回错误字符串缓冲区太小

如何从错误消息中获取实际的存储过程行号?

调用另一个存储过程后从存储过程返回错误结果

扩展存储过程xp_create_subdir返回“无效参数”消息

执行不返回结果的存储过程时引发错误

如何记录存储过程错误