我有一个非常大的代码库需要浏览和记录。目前,我正在通过在存储过程/视图中查找连接并写下详细信息来记录连接。非常耗时。
我很确定没有,但以防万一,有没有一种方法可以轻松地从 SQL 模块(视图/存储产品/函数/其他)中获取连接信息。
我知道
sys.dm_sql_referenced_entities
,但这告诉你使用了什么,而不是如何使用。
另一种选择是编写一个 SQL 解析器以及与之相关的所有内容,但我没有时间这样做,也没有时间使用现成的解析器并对其进行修改。
可以查询数据库系统表中的对象定义。
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 个存储过程。
也许有人知道 postgres 的相同查询?