动态 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_PROCEDURE
是 NULL
。
现在您在使用 +
的字符串连接中使用 @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()
代替 +
进行串联或手动替换 NULL
s。
但是无论如何你都应该避免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.
我有一个简单的存储过程,如下所示。如您所见,错误是在动态 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
returnsNULL
if the error did not occur within a stored procedure or trigger.
由于动态 SQL 既不是过程也不是触发器,这适用于此,您的 @ERROR_PROCEDURE
是 NULL
。
现在您在使用 +
的字符串连接中使用 @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()
代替 +
进行串联或手动替换 NULL
s。
但是无论如何你都应该避免RAISERROR
。请参阅 "RAISERROR (Transact-SQL)" 中的注释。
The
RAISERROR
statement does not honorSET XACT_ABORT
. New applications should useTHROW
instead ofRAISERROR
.
如果您使用的是SQLServer 2012以上版本,建议使用THROW.
将整个 CATCH 块更改为以下就足够了:
BEGIN CATCH
THROW
END CATCH;
Msg 8134, Level 16, State 1, Line 1
Divide by zero error encountered.