Sql server 动态创建的存储过程的注释中的SQL打印行号?

Sql server 动态创建的存储过程的注释中的SQL打印行号?,sql-server,tsql,dynamic-sql,line-numbers,Sql Server,Tsql,Dynamic Sql,Line Numbers,我编写了一个数千行的脚本,用于动态生成一些存储过程 我希望在存储过程的注释中引用生成存储过程的脚本,并希望能够通过将脚本文件的行号插入到存储过程文件的注释中来引用脚本文件中的行 例如,如果@line\u number在下面的代码中给出了我想要的行号,那么@line\u number应该是5 1| declare @job varchar(max) 2| SET @job = '/* this is generated dynamicly by _______ */' 3| SET @job =

我编写了一个数千行的脚本,用于动态生成一些存储过程

我希望在存储过程的注释中引用生成存储过程的脚本,并希望能够通过将脚本文件的行号插入到存储过程文件的注释中来引用脚本文件中的行

例如,如果@line\u number在下面的代码中给出了我想要的行号,那么@line\u number应该是5

1| declare @job varchar(max)
2| SET @job = '/* this is generated dynamicly by _______  */'
3| SET @job = @job + 'SELECT *' + CHAR(10)
4| SET @job = @job + 'FROM ' + @Table_Name + CHAR(10)
5| SET @job = @job + '/* ' + @@line_number + ' */'

还没有找到返回当前行号的内置函数,所以我已经开始制作一个函数来为我查找行号

如果我在顶部获取当前运行查询的文本并声明一些变量,然后复制并通过函数调用和@LineCounter增量代码,我可以获取当前行号

    DECLARE @var1 NVARCHAR(MAX) 
    SELECT @var1 = sqltext.TEXT
    FROM sys.dm_exec_requests req
    CROSS APPLY sys.dm_exec_sql_text(sql_handle) AS sqltext
    WHERE req.session_id = @@SPID
    DECLARE @LineCounter int
    SET @LineCounter = 0
    DECLARE @Current_Line_Number int
    SET @Current_Line_Number = 0
SET @LineCounter = @LineCounter + 1
SELECT @Current_Line_Number = [MSMS].[dbo].[ReturnLineNumber] (@var1, @LineCounter)
PRINT @Current_Line_Number
这就是函数

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author:      James J
-- Create date: 11/11/2013
-- Description: Function to return the line number for 
-- where the query was called from when passed the query 
-- an the count of the times it has already been used.
-- =============================================
ALTER FUNCTION ReturnLineNumber 
(
    @CurrentQuery nvarchar(max), 
    @Count  int
)
RETURNS int
AS
BEGIN
    DECLARE @var1 NVARCHAR(MAX) 
    DECLARE @functionName nvarchar(30)
    SET @functionName = 'ReturnLineNumber'
    SET @var1 = @CurrentQuery

    DECLARE @LineCount int
    SET @LineCount = 0

    IF (CHARINDEX(CHAR(13), @var1) > 0)
    BEGIN
        DECLARE @queryString nvarchar(max)
        SET @queryString = @var1
        DECLARE @LineIndex int
        SET @LineIndex = 1
        DECLARE @LineLength int
        DECLARE @linestring nvarchar(max)
        DECLARE @functioncount int
        SET @functioncount = 0
        WHILE (@LineIndex > 0)
        BEGIN
            SET @LineIndex = CHARINDEX(CHAR(13), @queryString)
            SET @LineLength = LEN(@queryString) - CHARINDEX(CHAR(13), @queryString)
            SET @linestring = SUBSTRING(@queryString, 0, @LineIndex + 1)
            SET @queryString = SUBSTRING(@queryString, @LineIndex + 1, @LineLength)
            SET @LineCount = @LineCount + 1
            IF (CHARINDEX(@functionName, @linestring) > 0)
            BEGIN
                SET @functioncount = @functioncount + 1
                IF (@functioncount = @Count)
                BEGIN
                    RETURN @LineCount
                END
            END
        END
    END
    RETURN 0
END
GO

这不是一种获取行号的好方法,我可能应该添加更多的检查以确保我没有注释掉函数调用,但这是目前我得到的最接近的方法。

您可以使用TRY/CATCH和强制错误,因为CATCH块可以通过error\u line()返回发生错误的行号功能。为可读性而格式化的完整结构为:

BEGIN TRY
    ;THROW 50000, 'Line#', 1 -- all 3 values are arbitrary, but required
END TRY
BEGIN CATCH
    SET @LineNumber = ERROR_LINE()
END CATCH
现在,要让@LineNumber变量填充其设置的行号,可以将该构造缩减为一行,如下所示:

BEGIN TRY;THROW 50000,'',1;END TRY BEGIN CATCH;SET @Line=ERROR_LINE();END CATCH
以下是it工作的完整示例:

SET ANSI_NULLS ON
SET NOCOUNT ON
GO
-- Line #1 (of current batch, not of the entire script if GOs are used)

DECLARE @CRLF NCHAR(2) = NCHAR(13) + NCHAR(10),
        @SQL1 NVARCHAR(MAX) = '',
        @SQL2 NVARCHAR(MAX) = '', -- Line #5
        @Line INT = -1 -- default to an invalid line #

SET @SQL1 += N'/********************' + @CRLF
SET @SQL1 += N' *' + @CRLF
SET @SQL1 += N' * Test Auto-' + @CRLF -- Line #10
SET @SQL1 += N' * Generated Proc 1' + @CRLF
BEGIN TRY;THROW 50000,'',1;END TRY BEGIN CATCH;SET @Line=ERROR_LINE();END CATCH
SET @SQL1 += N' * Line #:' + CONVERT(NVARCHAR(10), @Line) + @CRLF
SET @SQL1 += N' *' + @CRLF
SET @SQL1 += N' ********************/' + @CRLF -- Line #15

-- more code here

SET @SQL2 += N'/********************' + @CRLF
SET @SQL2 += N' *' + @CRLF -- Line #20
SET @SQL2 += N' * Test Auto-' + @CRLF
SET @SQL2 += N' * Generated Proc 2' + @CRLF
BEGIN TRY;THROW 50000,'',1;END TRY BEGIN CATCH;SET @Line=ERROR_LINE();END CATCH
SET @SQL2 += N' * Line #:' + CONVERT(NVARCHAR(10), @Line) + @CRLF
SET @SQL2 += N' *' + @CRLF -- Line #25
SET @SQL2 += N' ********************/' + @CRLF

PRINT @SQL1
PRINT @SQL2
GO
为Proc 1和Proc 2返回的行号分别为12和23,这两个行号都是正确的

请注意,THROW命令是在SQL Server 2012中启动的。如果您使用的是SQL Server 2005、2008或2008 R2,则需要使用RAISERROR()函数,而不是THROW。

我做了一些更改,以使其在2012年之前的SQL Server版本中工作:

DECLARE @Line INT
SET @Line = 0 BEGIN TRY RAISERROR ('Line#', 11, 1)WITH NOWAIT   END TRY BEGIN CATCH SET @Line=ERROR_LINE() END CATCH

PRINT('/* testing ... I messed up somewhere near line: ' + CONVERT(varchar(10), ISNULL(@Line, 0)) + ' */')

出于好奇,为什么要使用行号,因为行号会随着脚本的更新而改变。发生这种情况时,生成代码中的行号将不再指向生成代码时的行号。如果以1或2减去,这并不是什么大问题,但如果添加一个部分,则以20或30减去可能会导致混淆。为什么不使用“第1节”/“第2节,第5部分”等“标签”我正在写的主脚本做了很多非常相似的事情,所以我做了很多复制和过去的事情,然后改变了一些事情,欺骗了我自己在错误的地方搜索bug,因为我搜索了一个标题或注释,而这些标题或注释都是被复制的代码,或者因为我已经标记了相同的东西。我让脚本删除所有以前生成的脚本,并在每次运行时重新生成所有内容,因此,如果我可以输入行号,则在有任何更改时,它应始终更新。需要对此进行更多测试,因为它仍然出现错误。我希望使用
@@line
line()