从 SQL Server 中的视图/存储过程中提取联接信息

问题描述 投票:0回答:2

我有一个非常大的代码库需要浏览和记录。目前,我正在通过在存储过程/视图中查找连接并写下详细信息来记录连接。非常耗时。

我很确定没有,但以防万一,有没有一种方法可以轻松地从 SQL 模块(视图/存储产品/函数/其他)中获取连接信息。

我知道

sys.dm_sql_referenced_entities
,但这告诉你使用了什么,而不是如何使用。

另一种选择是编写一个 SQL 解析器以及与之相关的所有内容,但我没有时间这样做,也没有时间使用现成的解析器并对其进行修改。

sql-server dependencies
2个回答
1
投票

可以查询数据库系统表中的对象定义。

SELECT DISTINCT [obj].[name] AS [Object_Name], [obj].[type], [obj].[type_desc], [mod].[definition]
FROM sys.sql_modules AS [mod]
INNER JOIN sys.objects AS [obj]
         ON [mod].[object_id] = [obj].[object_id]
WHERE [mod].definition Like '%JOIN%'
AND [obj].[type] = 'P' OR [obj].[type] = 'V'

这仅搜索书面单词 JOIN,例如,如果在注释中使用该单词,在列名或其他任何内容中,它也会找到它。但这应该是了解要检查哪些程序(而不是手动检查每个程序)的良好开端。

在上面的查询中

[obj].[type] = 'P' 或 [obj].[type] = 'V'

使其仅查看 (SQL) 存储过程和视图。您可以省略此以获得更多潜在的定义。 Sys 对象的 Msdn 链接

现在尝试从这些定义中获取连接信息。这会很挑剔,如果有人知道更好的方法的话。

但是您可以做的是扫描过程定义。 在我的数据库中,我有以下过程用于说明目的:

CREATE PROCEDURE dbo.getFooBar AS
BEGIN
    SELECT * 
    FROM dbo.ADDRESSES AS ADR
    INNER JOIN dbo.CLIENTS AS CLT
        ON ADR.ClientId = CLT.Id
    WHERE ADR.City IS NOT NULL

    SELECT *
    FROM dbo.Clients AS CLT
    LEFT JOIN dbo.Users AS USR
        ON CLT.Id = USR.ClientId
    WHERE USR.Email IS NOT NULL
END

现在这是一个相当标准的查询,其中有一个 FROM 块,后面是 JOINS,然后是 WHERE。我们可以利用这些知识来扫描系统表中的定义列。

因为这是 SQL Server 2014(或 2016 之前的版本,其中 SQL Server 函数String_Split尚不存在),我们为此目的调整了自己的函数。基础取自here,然后进行修改。

IF OBJECT_ID('[dbo].[SPLIT_STRING]','IF') IS NULL BEGIN
    EXEC ('CREATE FUNCTION [dbo].[SPLIT_STRING] () RETURNS TABLE AS RETURN SELECT 1 X') 
END
GO
ALTER FUNCTION [dbo].[SPLIT_STRING]
(
    @string    nvarchar(MAX), 
    @separator nvarchar(MAX)
)
RETURNS TABLE WITH SCHEMABINDING 
AS RETURN
   WITH X(N) AS (SELECT 'Table1' FROM (VALUES (0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) T(C)),
        Y(N) AS (SELECT 'Table2' FROM X A1, X A2, X A3, X A4, X A5, X A6, X A7, X A8) , -- Up to 16^8 = 4 billion
        T(N) AS (SELECT TOP(ISNULL(LEN(@string),0)) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) -1 N FROM Y),
        Delim(Pos) AS (SELECT t.N FROM T WHERE (SUBSTRING(@string, t.N, LEN(@separator+'x')-1) LIKE @separator OR t.N = 0)),
        Separated(value) AS (SELECT SUBSTRING(@string, d.Pos, LEAD(d.Pos,1,2147483647) OVER (ORDER BY (SELECT NULL)) - d.Pos - LEN(@separator)) 
                               FROM Delim d
                              WHERE @string IS NOT NULL)
       SELECT s.value
         FROM Separated s
        WHERE s.value <> @separator
GO

这允许我们扫描定义,扫描包含单词 JOIN 的定义。在 FROM 上拆分那些(应该在 JOIN 之前)。然后直到 WHERE(或任何其他关键字)。然后再次过滤这些结果,仅查看再次具有 JOIN 关键字的定义。

SELECT [SUBQUERY].[Object_Name], [SUBQUERY].[Type], [split2].[value] AS DefinitionWithJoin FROM
(SELECT DISTINCT [obj].[name] AS [Object_Name], [obj].[type], [mod].[definition]
FROM sys.sql_modules AS [mod]
INNER JOIN sys.objects AS [obj]
         ON [mod].[object_id] = [obj].[object_id]
WHERE [mod].definition Like '%JOIN%'
AND [obj].[type] = 'P' OR [obj].[type] = 'V') AS SUBQUERY
CROSS APPLY dbo.[SPLIT_STRING](SUBQUERY.[definition],'FROM') AS SPLIT1
CROSS APPLY dbo.[SPLIT_STRING](SPLIT1.value, 'WHERE') AS SPLIT2
WHERE SPLIT2.value LIKE '%JOIN%'

本质上是剖析短语的对象定义。您可以将 SPLIT2 Cross Apply 中的语句更改为其他内容。请注意,由于各种字符串分割、扫描文本定义,这是一个不错的性能消耗。

但在我的结果中我会得到:

对象名称 类型 Join 定义
获取FooBar P 从 dbo.ADDRESSES AS ADR 内连接 dbo.CLIENTS AS ADR.ClientId = CLT.Id
获取FooBar P FROM dbo.Clients AS CLT LEFT JOIN dbo.Users AS USR ON CLT.Id = USR.ClientId

现在这种方法可能需要根据关键词进行一些调整。找到至少有 1 个 JOIN 语句的所有对象的第一个查询可用于仔细检查结果。但上述查询适用于我的本地测试 SQL Server 2014 DB,其中包含约 200 个存储过程。


0
投票

也许有人知道 postgres 的相同查询?

© www.soinside.com 2019 - 2024. All rights reserved.