【问题标题】:Dynamic SQL executed inside a stored procedure in a TRY/CATCH block but the error is not returned动态 SQL 在 TRY/CATCH 块中的存储过程内执行,但不返回错误
【发布时间】:2021-05-30 18:32:24
【问题描述】:

我有一个简单的存储过程,如下所示。如您所见,错误是在动态 SQL 内部强制生成的,并传递给 CATCH 块。问题是我期望的细节没有返回。我得到的只是这条消息:

消息 50000,级别 16,状态 1,过程 myproc,第 35 行 [批处理开始行 2]

为什么我不能取回 CATCH 块中编码的信息 - 过程名称、错误行和错误消息?

这是我的程序:

CREATE PROCEDURE myproc
AS
    SET XACT_ABORT ON
    BEGIN TRY
        DECLARE @nSQL NVARCHAR(MAX) = 'SELECT 1/0'
        EXEC sp_executesql @nSQL
    END TRY
    BEGIN CATCH
        DECLARE @ERROR_NUMBER INT;
        DECLARE @ERROR_SEVERITY INT;
        DECLARE @ERROR_STATE INT;
        DECLARE @ERROR_PROCEDURE NVARCHAR(128);
        DECLARE @ERROR_LINE INT;
        DECLARE @ERROR_MESSAGE NVARCHAR(4000);
  
        SET @ERROR_NUMBER = ERROR_NUMBER();
        SET @ERROR_SEVERITY = ERROR_SEVERITY();

        SELECT @ERROR_STATE = CASE 
                                 WHEN ERROR_STATE() = 0 THEN 1
                                 ELSE ERROR_STATE()
                              END;
        SET @ERROR_PROCEDURE = ERROR_PROCEDURE();
        SET @ERROR_LINE = ERROR_LINE();
        SET @ERROR_MESSAGE = 'ERROR in ' + @ERROR_PROCEDURE + '; (ERROR Line: ' + CAST(@ERROR_LINE AS NVARCHAR(5)) + '); ' + 'ERROR MESSAGE: '
                            + ERROR_MESSAGE() + ';';

        RAISERROR(@ERROR_MESSAGE, @ERROR_SEVERITY, @ERROR_STATE);
        RETURN (1);
    END CATCH;

【问题讨论】:

  • 接球有什么意义,只是为了再次加注?如果你想再加注,为什么不直接THROW;

标签: sql-server error-handling try-catch


【解决方案1】:

"ERROR_PROCEDURE (Transact-SQL)":

如果错误未发生在存储过程或触发器中,ERROR_PROCEDURE 将返回 NULL

由于动态 SQL 既不是过程也不是触发器,这适用于此处,您的 @ERROR_PROCEDURENULL

现在您在使用+ 的字符串连接中使用@ERROR_PROCEDURE,并将其分配给@ERROR_MESSAGE。如果您在"+ (String Concatenation) (Transact-SQL)" 中查找备注,您会发现这可能导致串联结果为NULL,而这很可能发生在您的情况中。

当您使用具有空值的字符串时,连接的结果取决于会话设置。就像对空值执行的算术运算一样,当将空值添加到已知值时,结果通常是未知值,对空值执行的字符串连接操作也应该产生空结果。但是,您可以通过更改当前会话的 CONCAT_NULL_YIELDS_NULL 设置来更改此行为。

因此您将NULL 作为RAISERROR 的第一个参数传递,因此不会打印任何消息。

您可以使用concat() 代替+ 进行连接或手动替换NULLs。

但无论如何你都应该避免RAISERROR。请参阅"RAISERROR (Transact-SQL)" 中的说明。

RAISERROR 语句不支持SET XACT_ABORT。新应用程序应使用THROW 而不是RAISERROR

【讨论】:

    【解决方案2】:

    ERROR_PROCEDURE()NULL 因为错误发生在动态 SQL 语句的内部范围,而不是存储过程本身。如果您想要当前 proc 的名称,请使用 OBJECT_NAME(@@PROCID)

    【讨论】:

      【解决方案3】:

      如果您使用的是SQL Server 2012或以上版本,建议使用THROW

      将整个 CATCH 块更改为以下内容就足够了:

      BEGIN CATCH
          THROW
      END CATCH;
      

      消息 8134,第 16 级,状态 1,第 1 行

      遇到除以零错误。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-04-09
        • 2016-01-28
        • 1970-01-01
        • 1970-01-01
        • 2022-01-06
        • 1970-01-01
        相关资源
        最近更新 更多