【问题标题】:Error Handling - Determine whether sp_executesql was the source错误处理 - 确定 sp_executesql 是否是源
【发布时间】:2019-04-26 16:21:49
【问题描述】:

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

在以下情况下,错误详细信息将错误标识为发生在第 1 行,但它们并不表示所引用的第 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           

【问题讨论】:

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


【解决方案1】:

很遗憾,调用堆栈不适用于 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】:

    你可以使用sp_executeSQLOUTPUT parameter

    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 中没有错误。

    【讨论】:

      【解决方案3】:

      这是返回行号的解决方案。我们在一个带有参数的 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;
      

      【讨论】:

        猜你喜欢
        • 2017-01-15
        • 1970-01-01
        • 2014-12-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-04-02
        相关资源
        最近更新 更多