如何找出哪个存储过程正在使用 SQL Server 中特定表的特定列?

Posted

技术标签:

【中文标题】如何找出哪个存储过程正在使用 SQL Server 中特定表的特定列?【英文标题】:How to find out which stored procedure is using the specific column of a specific table in SQL Server? 【发布时间】:2021-12-30 16:42:04 【问题描述】:

假设我有两张桌子:

CREATE TABLE Customers 
(
    Id BIGINT IDENTITY(1,1) PRIMARY KEY,
    FullName varchar(100) NOT NULL,
);

CREATE TABLE Employees 
(
    Id BIGINT IDENTITY(1,1) PRIMARY KEY,
    FullName varchar(100) NOT NULL,
);

如果我在任何存储过程中使用了Customers 表的FullName 列,那么我想通过搜索ColumnName = 'FullName'TableName = 'Customers' 来获取所有这些存储过程名称。

我不需要使用Employees 表中的FullName 列的那些存储过程名称。

是否可以在 SQL Server 中为此编写脚本?

注意:在我的搜索条件中,我想使用列名和表名进行搜索。

【问题讨论】:

【参考方案1】:

您不需要使用已弃用的sys.sql_dependencies,可以使用较新的sys.dm_sql_referenced_entities

不幸的是,经过一些测试,发现sys.sql_expression_dependenciessys.dm_sql_referencing_entities 没有返回有关列级依赖关系的信息。

SELECT OBJECT_SCHEMA_NAME(o.object_id),
       OBJECT_NAME(o.object_id)
FROM sys.objects o
JOIN sys.schemas s ON s.schema_id = o.schema_id
CROSS APPLY sys.dm_sql_referenced_entities(QUOTENAME(s.name) + '.' + QUOTENAME(o.name), 'OBJECT') d
INNER JOIN sys.columns c
    ON c.object_id = d.referenced_id
    AND c.column_id = d.referenced_minor_id
    AND c.object_id = OBJECT_ID('dbo.Customers')
    AND c.name = 'FullName'
WHERE o.type IN ('FN','TF','IF','P','V','TR');  -- scalar, multi-line and inline funcs, procs, views, triggers

【讨论】:

+1 用于提供非弃用选项。这样做的一个缺点或优点是,如果在已经破坏遗留问题的数据库中运行,它将返回不相关的错误。 (例如,有了这个对象CREATE FUNCTION [dbo].[LegacyNonsense]() RETURNS nvarchar(35) AS BEGIN RETURN (SELECT Foo FROM Bar) END 我没有从中得到任何错误。它应该给出什么错误?除了缺少架构和表引用之外,还有什么遗留或损坏的? 它引用了不存在的表和列。对于已经启动并运行了几年的任何数据库(因此“遗留问题”),哪种 IME 很常见 - 所以运行查询仍然会产生结果,但也会产生错误消息 i.stack.imgur.com/ZJobp.png @Charlieface 非常感谢您挽救了我的生命。实际上,当我们更改表的列名时,我需要这个查询。在使用您提供的此查询之前,我必须检查所有存储过程、函数、触发器和视图,以找到我们在哪里使用了该特定表的该列。现在它节省了我很多时间,让生活更轻松。【参考方案2】:

sys.sql_dependencies 已弃用,但您仍然可以使用它

SELECT OBJECT_SCHEMA_NAME(d.object_id),
       OBJECT_NAME(d.object_id)
FROM   sys.sql_dependencies d
       INNER JOIN sys.columns c
               ON c.object_id = d.referenced_major_id
                  AND c.column_id = d.referenced_minor_id
WHERE  d.class = 0
       AND d.referenced_major_id = object_id('dbo.Customers')
       AND c.name = 'FullName'

与文本搜索相比,这有很多优势,因为当过程同时包含字符串 CustomersFullName 但作为 cmets 或其他会产生误报的上下文时,您不会遇到问题。它还处理过程包含* 而不是明确提及名称的情况。

如果您使用动态 SQL,您可能需要退回到更痛苦的文本搜索。

我可能最终会使用混合方法来涵盖所有角度,如下所示,这样我就可以消除实际查看 sys.sql_dependencies 中找到的明确依赖项文本的需要,而只需要手动查看“可能”

WITH Candidates AS
(
SELECT d.object_id, 0 AS Source
FROM   sys.sql_dependencies d
       INNER JOIN sys.columns c
               ON c.object_id = d.referenced_major_id
                  AND c.column_id = d.referenced_minor_id
WHERE  d.class = 0
       AND d.referenced_major_id = object_id('dbo.Customers')
       AND c.name = 'FullName'
UNION ALL
SELECT m.object_id, 1 AS Source
FROM sys.sql_modules m
WHERE m.definition LIKE '%Customers%' AND (m.definition LIKE '%FullName%' OR m.definition LIKE '%*%')
)
SELECT SchemaName = OBJECT_SCHEMA_NAME(object_id),
       ModuleName = OBJECT_NAME(object_id),
       Confidence = IIF(MIN(Source) = 0, 'High', 'Possible')
FROM Candidates
GROUP BY object_id

【讨论】:

为什么不sys.sql_expression_dependencies @Charlieface - 没有相同的“每列”详细信息 @Charlieface i.stack.imgur.com/yfqca.png 看来你是对的。 sys.dm_sql_referencing_entities 也没有。但是sys.dm_sql_referenced_entities确实返回列级信息 @Charlieface - 是的。 TBH 我忘了那个。虽然使用起来很痛苦,因为它需要从具有潜在引用对象的结果中获得CROSS APPLY-ed,并且最终可能会引发错误【参考方案3】:

sys.dm_sql_referenced_entities 表将为您提供所需的信息。你可以在微软网站查看它的参考:SQL Server 2019 Reference

我喜欢通过文本搜索来完成这项工作。为此,您可以将所有存储过程脚本导出为平面文件,然后在文件中进行搜索。

您可以使用 Notepad++ 的“在文件中查找”功能来查找引用。您需要考虑到该列可能已使用不同的格式引用:."Date_Id"、.[Date_Id] 或 .Date_Id。

【讨论】:

【参考方案4】:

由于 SQL 的动态方面(对象之间的链接在创建对象时并不总是已知,除非使用 SCHEMABINDING 指令)链接在“执行”对象时完成。因此,确保您会发现所有带有另一个对象或对象属性的引用的 objetc 的唯一方法是使用内部标准进行命名......

Personnaly I use this convention for my business development (actually I do that for clubmed...)... 但它是法语的!

总结一下:

    所有表的名称都以 T_ 开头并以唯一的结尾 三元组(在表列表中是唯一的)。示例:T_CUSTOMER_CST 所有视图都有一个以 V_ 开头并以唯一名称结尾的名称 三元组(在视图列表中是唯一的)。示例:V_CUSTOMER_CST 所有表函数的名称都以 F_ 开头并以 a 结尾 唯一的三元组(在函数列表中是唯一的)。例子 : F_CUSTOMER_CST 表的所有内部数据列的名称都以 表的三元组。表 T_CUSTOMER_CST 的示例 客户是 CST_NAME,ID 是 CST_ID。 所有外部数据列(外键)保持原名,并且 如果它存在多个引用,则由一些完成 区别元素。表 T_JOURNEY_JRN 的 id 的示例 来自表 T_TOWN_TWN 的起始城镇和目标城镇是 TWN_ID_STARTS 和 TWN_ID_ENDS

等等……

因此,要找出在例程(过程、触发器、函数、视图...)中更改列定义的任何影响,要运行的查询是:

SELECT *
FROM   sys.sql_modules
WHERE  definition LIKE '%TWN?_ID%' ESCAPE '?' 

这将找到所有包含名为 TWN_ID 的列的对象。

【讨论】:

以上是关于如何找出哪个存储过程正在使用 SQL Server 中特定表的特定列?的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server:通过查询找出列的默认值

JPA 2.1在sql server中调用存储过程

在SQL server中如何写存储过程

如何在sql server的另一个存储过程中执行存储过程

如何将 SQL Server 存储过程迁移到 Mysql [关闭]

如何在 Python 中使用表值参数访问 SQL Server 2008 存储过程