代码之家  ›  专栏  ›  技术社区  ›  Ben Gribaudo

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

  •  1
  • Ben Gribaudo  · 技术社区  · 5 年前

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

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

    我正在寻找某种方法来识别源代码,以便相应地记录它。我想记录一下 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           
    
    
    0 回复  |  直到 5 年前
        1
  •  1
  •   Dan Guzman    5 年前

    不幸的是,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
  •   Junc Mayl    5 年前

    你可以用 sp_executeSQL 是的 OUTPUT 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都在一行上,这使整个事情变得更复杂,但你明白了。。。

    编辑:如果 @错误线路 NULL ,没有错误 sp_executeSQL .

        3
  •  0
  •   CubeSpark    3 年前

    下面是返回行号的解决方案。我们正处在一个需要参数的大过程中。本质上,作为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;