Sql server 是否可以在T-SQL代码中检测游标(嵌套游标)?

Sql server 是否可以在T-SQL代码中检测游标(嵌套游标)?,sql-server,tsql,Sql Server,Tsql,我希望找到一种在存储过程中发现潜在低效T-SQL的方法,在本例中,不仅检测存储过程中的游标,而且最好检测嵌套游标 下面的脚本基于sys.dm_sql_引用的_实体,从给定的起始存储过程中,我可以看到一个递归的下游调用堆栈,包括一列,指示是否在过程定义中找到了文本光标 这很有帮助,但它不能告诉我: 一个过程中是否存在多个游标 是否使用嵌套游标(当然,真正完美的解决方案还必须从游标中检测对包含游标的存储过程的调用) 我认为能够做到这一点可能超出了查询sys表的能力,并且涉及到对SQL本身的解析——是

我希望找到一种在存储过程中发现潜在低效T-SQL的方法,在本例中,不仅检测存储过程中的游标,而且最好检测嵌套游标

下面的脚本基于sys.dm_sql_引用的_实体,从给定的起始存储过程中,我可以看到一个递归的下游调用堆栈,包括一列,指示是否在过程定义中找到了文本光标

这很有帮助,但它不能告诉我:

  • 一个过程中是否存在多个游标
  • 是否使用嵌套游标(当然,真正完美的解决方案还必须从游标中检测对包含游标的存储过程的调用)
  • 我认为能够做到这一点可能超出了查询sys表的能力,并且涉及到对SQL本身的解析——是否有人知道可以实现这一点的技术或工具,或者可能有一种完全不同的方法可以告诉我相同的信息

    DECLARE @procname varchar(30)
    SET @procname='dbo.some_root_procedure_name'
    ;WITH CTE([DB],[OBJ],[INDENTED_OBJ],[SCH],[lvl],[indexof_cursor],[referenced_object_definition])
    AS
    (
        SELECT referenced_database_name AS [DB],referenced_entity_name AS [OBJ],
        cast(space(0) + referenced_entity_name as varchar(max)) AS [INDENTED_OBJ],
        referenced_schema_name AS [SCH],0 AS [lvl]
        ,charindex('cursor',object_definition(referenced_id)) as indexof_cursor
        ,object_definition(referenced_id) as [referenced_object_definition]
        FROM sys.dm_sql_referenced_entities(@procname, 'OBJECT') 
        INNER JOIN sys.objects as o on o.object_id=OBJECT_ID(referenced_entity_name)
        WHERE o.type IN ('P','FN','IF','TF') 
    UNION ALL 
        SELECT referenced_database_name AS [DB],referenced_entity_name AS [OBJ],
        cast(space(([lvl]+1)*2) + referenced_entity_name as varchar(max)) AS [INDENTED_OBJ],
        referenced_schema_name AS [SCH],[lvl]+1 as [lvl]
        ,charindex('cursor',object_definition(referenced_id)) as indexof_cursor
        ,object_definition(referenced_id) as [referenced_object_definition]
        FROM CTE as c CROSS APPLY
        sys.dm_sql_referenced_entities(c.SCH+'.'+c.OBJ, 'OBJECT') as ref
        INNER JOIN sys.objects as o on o.object_id=OBJECT_ID(referenced_entity_name)
        WHERE o.type IN ('P','FN','IF','TF') and ref.referenced_entity_name NOT IN (c.OBJ)  -- Exit Condition
    ) 
    SELECT 
    * 
    FROM CTE
    

    编辑:我将此标记为“已解决”,尽管我认为可以对以下解决方案进行一些改进-我认为对于大多数场景来说它“足够好”,但我认为完全递归的解决方案可以遍历“无限”深度调用链。

    也许有更有效的方法,但是你可以搜索程序代码。虽然它不是万无一失的,因为它可能会得到一些误报,但你不应该错过任何一个。它不会忽略注释和变量名,因此很有可能获取一些额外的内容

    SELECT name, xtype, colid, text
    into #CodeBlocks
    FROM dbo.sysobjects left join .dbo.syscomments 
    ON dbo.sysobjects.id = .dbo.syscomments.id
    where xtype = 'P'
    order by 1
    
    SELECT name,
            (SELECT convert(varchar(max),text)  
            FROM #CodeBlocks t2
            WHERE t1.name = t2.name  
            ORDER BY t2.colid
            FOR XML PATH('')
            ) text
    into #AllCode
    FROM #CodeBlocks t1
    GROUP BY name
    
    select #AllCode.name,
        case when InterProc.name is not null then
            'Possible Inter-Proc Nesting'
        when #AllCode.text like '%CURSOR%FOR%CURSOR%FOR%DEALLOCATE%DEALLOCATE%' then
            'Possible Nested Cursor'
        when #AllCode.text like '%CURSOR%FOR%CURSOR%FOR%' then
            'Possible Multiple Cursor Used'
        ELSE
            'Possible Cursor Used'
        end
    from #AllCode
    left join #AllCode InterProc
    on InterProc.text like '%CURSOR%FOR%'
        and #AllCode.text like '%CURSOR%FOR%' + InterProc.name + '%DEALLOCATE%'
    where #AllCode.text like '%CURSOR%FOR%'
    

    我在我们的服务器上发现了一些我不知道的嵌套游标。有趣的。:)

    是否检查了嵌套游标的执行计划?或者,甚至不用麻烦追踪嵌套的游标;只需跟踪未执行的过程和缓存的执行计划。你会得到更好的回报,因为你不会被效率低下的程序分散注意力,因为这些程序无法访问足够的数据成为真正的问题。我不一定不同意,但我正在寻找当前或未来风险的可能性。或者另一种说法:如果我的工具箱中有这个查询,我会运行它吗?不管当前有什么性能问题?这实际上非常聪明!它不能捕获过程间嵌套的游标,但我想知道,是否可以通过某种方式将我上面的查询的交叉应用连接与此结合起来,以某种方式实现这一点。。。这是个好办法!我没有想到过程间嵌套。。。。哇!我想你可以做一个自连接来查找引用。只会发现过程间嵌套的深度只有一层,但您可以对其进行扩展,以便对更深的嵌套进行递归搜索。添加到回答中。我上面的评论不正确-相反,我认为需要在SP文本中执行的操作是,用该进程的定义替换dbo.SomeProcName,如果您递归该进程,您将在一条语句中得到整个“过程调用链”,然后在该语句中使用case语句中的逻辑,您将实现完整的进程间嵌套检测。至少我这么认为。如果是这样的话,我有一个不相关但功能上有用的查询,我必须看一下。这已经相当不错了,但是如果能够处理完整的调用堆栈,那将是一个了不起的主意。这是一个好主意!您只需要在一些循环检测中编程来处理递归过程的任何情况。看起来,在手之前解析出所有评论并消除大部分错误否定并不困难。