动态 SQL 在 TRY/CATCH 块中的存储过程中执行,但未返回错误

Dynamic SQL executed inside a stored procedure in a TRY/CATCH block but the error is not returned

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

Msg 50000, Level 16, State 1, Procedure myproc, Line 35 [Batch Start Line 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;

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

"ERROR_PROCEDURE (Transact-SQL)":

ERROR_PROCEDURE returns NULL if the error did not occur within a stored procedure or trigger.

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

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

When you work with strings with a null value, the result of the concatenation depends on the session settings. Just like arithmetic operations that are performed on null values, when a null value is added to a known value the result is typically an unknown value, a string concatenation operation that is performed with a null value should also produce a null result. However, you can change this behavior by changing the setting of CONCAT_NULL_YIELDS_NULL for the current session.

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

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

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

The RAISERROR statement does not honor SET XACT_ABORT. New applications should use THROW instead of RAISERROR.

如果您使用的是SQLServer 2012以上版本,建议使用THROW.

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

BEGIN CATCH
    THROW
END CATCH;

Msg 8134, Level 16, State 1, Line 1

Divide by zero error encountered.