【问题标题】:How can I get a list of all columns referenced in a stored procedure?如何获取存储过程中引用的所有列的列表?
【发布时间】:2015-05-15 10:05:29
【问题描述】:

我有许多使用 CTE、临时表、表变量和子查询的存储过程,我需要获取存储过程中使用的所有列(包括数据库、模式和表/视图)的列表。我不需要获取临时表、表变量或 CTE 中的列。我只需要在我的服务器上的数据库中的表或视图中定义的引用列。

我尝试了sys.dm_sql_referenced_entitiessys.sql_expression_dependencies,但它们在第一次选择查询后或在 CTE 中选择后不返回列。

【问题讨论】:

    标签: sql sql-server tsql stored-procedures sql-server-2012


    【解决方案1】:

    当一个存储过程被执行时,它被解析并编译成一个查询计划,它被缓存起来,你可以通过 XML 格式的 sys.dm_exec_cached_plans 和 sys.dm_exec_query_plan 访问它。查询计划记录了解析代码的每个部分的“输出列表”。查看存储过程使用了哪些列只需查询此 XML,如下所示:

    --Execute the stored procedure to put its query plan in the cache
    exec sys.sp_columns ''
    
    DECLARE @TargetObject nvarchar(100) = 'sys.sp_columns';
    
    WITH XMLNAMESPACES (
        'http://schemas.microsoft.com/sqlserver/2004/07/showplan' as ns1
    ), CompiledPlan AS (
        SELECT 
            (SELECT query_plan FROM sys.dm_exec_query_plan(cp.plan_handle)) qp,
            (SELECT ObjectID FROM sys.dm_exec_sql_text(cp.plan_handle)) ob
        FROM sys.dm_exec_cached_plans cp
        WHERE objtype = 'Proc'
    ), ColumnReferences AS (
        SELECT DISTINCT
            ob,
            p.query('.').value('./ns1:ColumnReference[1]/@Database', 'sysname') AS [Database],
            p.query('.').value('./ns1:ColumnReference[1]/@Schema', 'sysname') AS [Schema],
            p.query('.').value('./ns1:ColumnReference[1]/@Table', 'sysname') AS [Table],
            p.query('.').value('./ns1:ColumnReference[1]/@Column', 'sysname') AS [Column]
        FROM CompiledPlan
            CROSS APPLY qp.nodes('//ns1:ColumnReference') t(p)
    )
    
    SELECT 
        [Database], 
        [Schema], 
        [Table], 
        [Column]
    FROM ColumnReferences 
    WHERE 
        [Database] IS NOT NULL AND 
        ob = OBJECT_ID(@TargetObject, 'P')
    

    买者警告这取决于您如何定义“二手”。可能是存储过程中的 CTE 引用了表中的 5 列,但是当使用此 CTE 时,仅传递了其中的三列。查询优化器可能忽略这些额外的字段并且不将它们包含在计划中。另一方面,优化器可能会决定它可以通过在输出中包含额外的字段来进行更有效的查询,以使其能够在以后使用更好的索引。此代码将返回查询计划使用的列,它们可能不完全是存储过程代码中的列。

    【讨论】:

    • 这就是我想要的。仅供参考-对我来说运行缓慢(3分钟+)。您可能希望将 OBJECT_ID 过滤器移至 CompiledPlan CTE。
    • 有趣,你能对代码进行更改并重试吗? CTE 只是语法糖,优化器应该在它认为最好的地方进行过滤。 Exec语句是否需要一些时间?
    • 最重要的同步是 CompiledPlan CTE。如果我把对象过滤器放在那里,那么它需要的时间会少一点,但其他一切都会快得多。
    【解决方案2】:

    这是我用来获取存储过程中使用的所有表的方法,但它不返回列。

        declare
            @target_table as varchar(255) = N'my_sp',
            @target_schema as varchar(255) = N'dbo';
    
        select distinct od.name as depname
            ,1 as depnumber
            ,od.type as deptype
            ,sd.name as depschemaname
        from sys.objects as o
        inner join sys.schemas as s
            on s.schema_id = o.schema_id
        inner join sys.objects as od
            on od.object_id = o.parent_object_id
        inner join sys.schemas as sd
            on sd.schema_id = od.schema_id
        where s.name = @target_schema
            and o.name = @target_table and od.type = 'U'
    
        union
    
        select distinct od.name as depname
            ,case 
                when od.type = 'P'
                    then np.procedure_number
                else 1
                end as depnumber
            ,od.type as deptype
            ,sd.name as depschemaname
        from sys.sql_expression_dependencies d
        inner join sys.objects o
            on o.object_id = d.referencing_id
        inner join sys.schemas s
            on s.schema_id = o.schema_id
        inner join sys.objects od
            on od.object_id = d.referenced_id
        inner join sys.schemas sd
            on sd.schema_id = od.schema_id
        left join sys.numbered_procedures np
            on np.object_id = d.referenced_id
        where s.name = @target_schema
            and o.name = @target_table and od.type = 'U';
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-12-27
      • 1970-01-01
      • 2012-01-29
      • 2011-10-20
      • 2013-11-01
      • 2015-09-18
      • 2014-04-21
      相关资源
      最近更新 更多