错误处理-确定sp_executesql是否为源

时间:2019-04-26 16:21:49

标签: sql-server tsql error-handling sp-executesql

sp_executesql返回错误消息时,是否有任何内置的错误处理方法/机制可用于标识此错误是由该过程返回的,还是直接从包含脚本的错误返回的? / p>

在以下情况下,错误详细信息将错误标识为发生在第1行,但它们并不表示所引用的第1行是不是行中的第行脚本本身 ,而是由脚本传递给sp_executesql 的查询中的第1行

我正在寻找识别来源的方法,以便可以相应地对其进行记录。我想记录类似Script x - Call to inner query errored on that query's line 1之类的内容,而不是一般的(且具有误导性的)Script x errored on line 1

演示

-- do other things first

BEGIN TRY  
    EXECUTE sp_executesql @stmt = N'SELECT 1/0';
END TRY
BEGIN CATCH  
    SELECT ERROR_NUMBER() AS ErrorNumber
         ,ERROR_SEVERITY() AS ErrorSeverity
         ,ERROR_STATE() AS ErrorState
         ,ERROR_PROCEDURE() AS ErrorProcedure
         ,ERROR_LINE() AS ErrorLine
         ,ERROR_MESSAGE() AS ErrorMessage
END CATCH; 

返回:

ErrorNumber ErrorSeverity ErrorState  ErrorProcedure 
----------- ------------- ----------- ---------------
8134        16            1           NULL           

3 个答案:

答案 0 :(得分:0)

您可以使用sp_executeSQL的{​​{3}}:

DECLARE @ErrorLine NVARCHAR(32)
DECLARE @Params NVARCHAR(150) = '@Return INT OUTPUT'
DECLARE @SQL NVARCHAR(MAX) = ''


SET @SQL = @SQL + '     '
SET @SQL = @SQL + '    BEGIN TRY '
SET @SQL = @SQL + '        SELECT 100 AS First '
SET @SQL = @SQL + '        SELECT 1/0 AS Second '
SET @SQL = @SQL + '    END TRY '
SET @SQL = @SQL + '    BEGIN CATCH '
SET @SQL = @SQL + '        SELECT @Return = ERROR_LINE() '
SET @SQL = @SQL + '    END CATCH '
SET @SQL = @SQL + '     '

EXEC sp_executeSQL @SQL, @Params, @Return = @ErrorLine OUTPUT

SELECT @ErrorLine

无论错误在哪里,这段代码都将显示@ErrorLine = 1,因为从技术上讲,整个SQL都在一行上,这使整个事情变得更加复杂,但是您明白了...

编辑:如果@ErrorLineNULL,则sp_executeSQL中没有错误。

答案 1 :(得分:0)

不幸的是,调用堆栈不适用于T-SQL错误处理。考虑升级this feature request以便于捕获T-SQL堆栈详细信息。

下面的示例在内部脚本出错时使用嵌套的TRY / CATCH引发用户定义的错误(消息号50000),捕获可用的详细信息以及上下文描述(“内部脚本”)。当外部脚本中发生错误时,只需重新抛出原始错误即可。缺少上下文和系统错误号表示最外面的脚本犯了错误,尽管您可以在那里构建并引发用户定义的错误,包括外部脚本上下文描述。

BEGIN TRY
    BEGIN TRY

        EXECUTE sp_executesql @stmt = N'SELECT 1/0;';
    END TRY
    BEGIN CATCH  

        DECLARE
             @ErrorNumber int
            ,@ErrorMessage nvarchar(2048)
            ,@ErrorSeverity int
            ,@ErrorState int
            ,@ErrorLine int;

        SELECT
             @ErrorNumber =ERROR_NUMBER()
            ,@ErrorMessage =ERROR_MESSAGE()
            ,@ErrorSeverity = ERROR_SEVERITY()
            ,@ErrorState =ERROR_STATE()
            ,@ErrorLine =ERROR_LINE();

        RAISERROR('Error %d caught in inner script at line %d: %s'
            ,@ErrorSeverity
            ,@ErrorState
            ,@ErrorNumber
            ,@ErrorLine
            ,@ErrorMessage);

    END CATCH; 
END TRY
BEGIN CATCH  

    THROW;

END CATCH; 
GO

答案 2 :(得分:0)

这是返回行号的解决方案。我们在一个接受参数的 SPROC 中。本质上,作为 tSQL 开发人员,您需要猜测问题会在哪里发生,通常是围绕形式参数输入的参数。

-- Preamble
CREATE PROCEDURE [Meta].[ValidateTable]
@DatabaseNameInput VARCHAR(100), -- = 'DatabaseNameInput',
@TableNameInput VARCHAR(100), --  = 'TableNameInput',
@SchemaNameInput VARCHAR(100), --  = 'SchemaNameInput',
AS
BEGIN
DECLARE @crlf CHAR(2) = CHAR(13) + CHAR(10),

-----------Database Validity------------------
@IsDatabaseValid BIT,
@DatabaseNumber INTEGER,
@DatabaseNamePredicate NVARCHAR(100),
@CurrentExecutingContext NVARCHAR(40),
@DatabaseValidityExecutable NVARCHAR(100),
@DatabaseParameterString NVARCHAR(50),

-----------Table Validity------------------
@TableObjectIdentity INTEGER,
@TableString NVARCHAR(500),
@TableParameterString NVARCHAR(50),
@TableValidityExecutable NVARCHAR(200),

-----------Error Handling------------------
@ErrorState INTEGER = 0,
@ErrorNumber INTEGER = 0,
@ErrorSeverity INTEGER = 0,
@MyErrorMessage NVARCHAR(150),
@SetMessageText NVARCHAR(1024) = 'No Error Message Text for sys.messages.',
@ErrorDescription NVARCHAR(1024) = 'No error description was given.';

-- Be aware of SQL Injection Risk with no semi-colons at the line tails
SET @TableString = 'N' + '''' + @DatabaseNameInput  + '.' + @SchemaNameInput + '.' + @TableNameInput + '''';
SET @DatabaseParameterString = N'@DatabaseNumber INTEGER OUTPUT ';
SET @TableParameterString = N'@TableObjectIdentity INTEGER OUTPUT';

-- Phase 0.0, testing for database existence.
PRINT 'Table Validity Executable: ' + @TableValidityExecutable;
EXECUTE sp_executesql @DatabaseValidityExecutable, @DatabaseParameterString, @DatabaseNumber = @DatabaseNumber OUTPUT;

IF @DatabaseNumber IS NULL 
BEGIN 
  SET @MyErrorMessage = 'The @DatabaseNameInput parameter: "%s" specified by the caller does not exist on this SQL Server - ' + @@SERVERNAME;
  EXECUTE sys.sp_addmessage @msgnum = 59802, @severity = 16, @msgtext = @MyErrorMessage, @replace = 'replace', @lang = 'us_english';   
  RAISERROR(59802, 15, 1, @DatabaseNamePredicate);
END;

-- Phase 0.1, testing for table existence.
PRINT 'Table Validity Executable: ' + @TableValidityExecutable;
EXECUTE sp_executesql @TableValidityExecutable, @TableParameterString, @TableObjectIdentity = @TableObjectIdentity OUTPUT;
IF @TableObjectIdentity IS NULL 
BEGIN 
  SET @MyErrorMessage = 'The @TableNameInput parameter: "%s" specified by the caller does not exist in this database - ' + DB_NAME() +';';
  EXECUTE sys.sp_addmessage @msgnum = 59803, @severity = 16, @msgtext = @MyErrorMessage, @replace = 'replace', @lang = 'us_english';   
  RAISERROR(59803, 15, 1, @TableString);
END;