如何在SQL Server中递归获取存储过程内的嵌套函数列表?
嘿,你已经搞定了最基础的一步——从存储过程里揪出直接调用的函数,这已经很棒了!接下来要实现函数的递归嵌套检索,核心思路其实很简单:把你已经找到的函数当作新的「检索目标」,重复你之前用来提取函数的逻辑,直到挖不出新的函数为止。下面我给你两种实用的实现思路,你可以根据你的数据库环境来选:
方法一:用递归CTE(适合SQL Server、PostgreSQL等支持的数据库)
如果你的数据库支持递归公共表表达式(CTE),这是最简洁的实现方式。假设你之前是用系统视图来提取函数调用的,那可以直接套递归逻辑:
-- 递归CTE:层层挖掘函数的嵌套调用 WITH RecursiveFunctionCalls AS ( -- 锚点成员:从存储过程提取直接调用的函数 SELECT OBJECT_NAME(sp.object_id) AS ParentObject, sp.object_id AS ParentObjectId, OBJECT_NAME(re.referenced_id) AS CalledFunction, re.referenced_id AS CalledFunctionId, 1 AS Depth -- 标记层级,1表示存储过程直接调用 FROM sys.procedures sp CROSS APPLY sys.dm_sql_referenced_entities(OBJECT_SCHEMA_NAME(sp.object_id) + '.' + OBJECT_NAME(sp.object_id), 'OBJECT') re WHERE re.referenced_entity_name IS NOT NULL AND re.referenced_class_desc = 'SQL_SCALAR_FUNCTION' -- 可按需调整为TABLE_VALUED_FUNCTION或去掉筛选所有函数 UNION ALL -- 递归成员:从已发现的函数里提取它们调用的函数 SELECT OBJECT_NAME(rfc.CalledFunctionId) AS ParentObject, rfc.CalledFunctionId AS ParentObjectId, OBJECT_NAME(re.referenced_id) AS CalledFunction, re.referenced_id AS CalledFunctionId, rfc.Depth + 1 AS Depth FROM RecursiveFunctionCalls rfc CROSS APPLY sys.dm_sql_referenced_entities(OBJECT_SCHEMA_NAME(rfc.CalledFunctionId) + '.' + OBJECT_NAME(rfc.CalledFunctionId), 'OBJECT') re WHERE re.referenced_entity_name IS NOT NULL AND re.referenced_class_desc = 'SQL_SCALAR_FUNCTION' -- 关键:避免循环引用(比如函数A调用B,B又调用A)导致无限递归 AND NOT EXISTS ( SELECT 1 FROM RecursiveFunctionCalls rc WHERE rc.ParentObjectId = re.referenced_id AND rc.CalledFunctionId = rfc.ParentObjectId ) ) -- 输出所有层级的函数调用关系 SELECT * FROM RecursiveFunctionCalls ORDER BY Depth, ParentObject;
代码解释:
- 锚点成员:就是你之前已经实现的逻辑——从存储过程中提取直接调用的函数,同时标记层级为1。
- 递归成员:把上一层找到的
CalledFunction当作新的父对象,重复调用提取逻辑,层级加1。 - 循环引用处理:通过判断避免互为调用的函数导致无限递归。
方法二:循环迭代法(通用所有数据库)
如果你的数据库不支持递归CTE,或者你更倾向于可控性更强的方式,可以用临时表+循环的方式实现:
-- 创建临时表存储所有函数调用关系,标记是否已处理 CREATE TABLE #FunctionCalls ( ParentObject NVARCHAR(128), ParentObjectId INT, CalledFunction NVARCHAR(128), CalledFunctionId INT, Depth INT, Processed BIT DEFAULT 0 -- 0=未处理,1=已处理 ); -- 第一步:插入存储过程直接调用的函数 INSERT INTO #FunctionCalls (ParentObject, ParentObjectId, CalledFunction, CalledFunctionId, Depth) SELECT OBJECT_NAME(sp.object_id), sp.object_id, OBJECT_NAME(re.referenced_id), re.referenced_id, 1 FROM sys.procedures sp CROSS APPLY sys.dm_sql_referenced_entities(OBJECT_SCHEMA_NAME(sp.object_id) + '.' + OBJECT_NAME(sp.object_id), 'OBJECT') re WHERE re.referenced_entity_name IS NOT NULL AND re.referenced_class_desc = 'SQL_SCALAR_FUNCTION'; -- 循环处理未处理的函数,直到没有新函数被发现 WHILE EXISTS (SELECT 1 FROM #FunctionCalls WHERE Processed = 0 AND ParentObjectId IN (SELECT CalledFunctionId FROM #FunctionCalls)) BEGIN -- 插入当前未处理函数调用的新函数 INSERT INTO #FunctionCalls (ParentObject, ParentObjectId, CalledFunction, CalledFunctionId, Depth) SELECT OBJECT_NAME(fc.CalledFunctionId), fc.CalledFunctionId, OBJECT_NAME(re.referenced_id), re.referenced_id, fc.Depth + 1 FROM #FunctionCalls fc CROSS APPLY sys.dm_sql_referenced_entities(OBJECT_SCHEMA_NAME(fc.CalledFunctionId) + '.' + OBJECT_NAME(fc.CalledFunctionId), 'OBJECT') re WHERE fc.Processed = 0 AND re.referenced_entity_name IS NOT NULL AND re.referenced_class_desc = 'SQL_SCALAR_FUNCTION' -- 避免重复插入相同的调用关系 AND NOT EXISTS ( SELECT 1 FROM #FunctionCalls fc2 WHERE fc2.ParentObjectId = fc.CalledFunctionId AND fc2.CalledFunctionId = re.referenced_id ); -- 标记这些函数为已处理,避免重复解析 UPDATE #FunctionCalls SET Processed = 1 WHERE Processed = 0 AND ParentObjectId IN (SELECT CalledFunctionId FROM #FunctionCalls); END -- 查看最终结果 SELECT ParentObject, CalledFunction, Depth FROM #FunctionCalls ORDER BY Depth, ParentObject; -- 清理临时表 DROP TABLE #FunctionCalls;
代码解释:
- 用临时表存储所有已发现的调用关系,通过
Processed字段标记哪些函数已经被解析过。 - 每次循环只处理未解析的函数,把它们调用的新函数插入表中,直到没有新的函数可以添加。
- 同样加入了重复调用和循环引用的判断,保证逻辑稳定。
关键注意事项
- 函数类型筛选:根据你的需求调整
referenced_class_desc的值,比如要包含表值函数就改成'TABLE_VALUED_FUNCTION',去掉该条件则会提取所有类型的函数。 - 权限问题:确保你的数据库账号有访问系统视图(如
sys.procedures、sys.dm_sql_referenced_entities)的权限,否则会查询不到数据。 - 自定义解析逻辑:如果你之前是通过解析存储过程的文本定义(比如用字符串函数找函数名)来提取函数的,只需要把上述代码中调用系统视图的部分替换成你的解析逻辑即可,核心思路不变——重复解析已发现的函数定义。
内容的提问来源于stack exchange,提问作者JJ.
相关产品推荐
相关产品推荐

