多父列层级记录排序:Recursive CTE的修改方案及示例
多父列自关联表的递归CTE实现需求
我有一张自关联表,包含2个(可能更多)外键列用于定义记录间的层级关系,表结构及数据如下:
| Id | 名称 | Parent1__c | Parent2__c |
|---|---|---|---|
| a00Hu000014KBJrIAO | 祖父节点 | NULL | NULL |
| a00Hu000014KBJwIAO | 父节点1 | a00Hu000014KBJrIAO | NULL |
| a00Hu000014KBK1IAO | 父节点2 | a00Hu000014KBJrIAO | NULL |
| a00Hu000014KBK6IAO | 子节点1_1 | a00Hu000014KBJwIAO | NULL |
| a00Hu000014KBK7IAO | 孙节点1_1_1 | a00Hu000014KBK6IAO | a00Hu000014KBK1IAO |
| a00Hu000014KBvaIAG | 子节点2_2 | NULL | a00Hu000014KBK1IAO |
| a00Hu000014KBvfIAG | 孙节点2_2_2 | a00Hu000014KBJwIAO | a00Hu000014KBvaIAG |
| a00Hu000014KBvkIAG | 孙节点1_1_2 | a00Hu000014KBK6IAO | NULL |
我已经实现了单父列(Parent1__c)的递归CTE,但不知道如何修改以同时支持Parent1__c和Parent2__c两个父列,最终希望按层级对记录排序,预期结果如下:
| Id | RecordHierarchyLevel |
|---|---|
| a00Hu000014KBJrIAO | 0 |
| a00Hu000014KBJwIAO | 1 |
| a00Hu000014KBK1IAO | 1 |
| a00Hu000014KBK6IAO | 2 |
| a00Hu000014KBvaIAG | 2 |
| a00Hu000014KBvfIAG | 3 |
| a00Hu000014KBvkIAG | 3 |
| a00Hu000014KBK7IAO | 3 |
之前用C#递归查询会产生大量请求,对百万级数据集不友好,希望用原生SQL的递归CTE解决。现有单父列递归CTE代码如下:
With Children AS ( SELECT Id, Name, Parent1__c, Parent2__c, 0 AS Level FROM CustomSelfRel__c WHERE Id = 'a00Hu000014KBJrIAO' AND NSPRemoved IS NULL UNION ALL SELECT T.Id, T.Name, T.Parent1__c, T.Parent2__c, Level + 1 AS Level FROM CustomSelfRel__c AS T JOIN Children C ON C.Id = T.Parent1__c ) SELECT * FROM Children;
多父列递归CTE解决方案
要支持多个父列,核心是在递归关联时同时匹配所有可能的父列,并且需要避免重复处理同一记录(因为一个子节点可能同时属于多个父节点)。以下是修改后的递归CTE代码:
WITH Hierarchy AS ( -- 锚点成员:根节点 SELECT Id, Name, Parent1__c, Parent2__c, 0 AS RecordHierarchyLevel, -- 用字符串记录已访问的节点,避免重复处理 CAST(',' + Id + ',' AS VARCHAR(MAX)) AS VisitedNodes FROM CustomSelfRel__c WHERE Id = 'a00Hu000014KBJrIAO' AND NSPRemoved IS NULL UNION ALL -- 递归成员:匹配所有父列的子节点 SELECT T.Id, T.Name, T.Parent1__c, T.Parent2__c, H.RecordHierarchyLevel + 1 AS RecordHierarchyLevel, -- 更新已访问节点集合 H.VisitedNodes + T.Id + ',' AS VisitedNodes FROM CustomSelfRel__c AS T JOIN Hierarchy H ON (H.Id = T.Parent1__c OR H.Id = T.Parent2__c) -- 确保当前节点未被访问过,避免循环和重复 AND CHARINDEX(',' + T.Id + ',', H.VisitedNodes) = 0 ) -- 最终查询:去重并按层级排序 SELECT DISTINCT Id, RecordHierarchyLevel FROM Hierarchy ORDER BY RecordHierarchyLevel, Id;
关键修改说明
- 多父列关联条件:在递归JOIN时,用
OR同时匹配Parent1__c和Parent2__c,确保所有通过任一父列关联的子节点都能被纳入层级。 - 避免重复与循环:新增
VisitedNodes字段记录已处理的节点ID,通过CHARINDEX检查当前节点是否已被访问,防止同一节点被多次处理(比如孙节点1_1_1同时属于两个父节点),也避免潜在的循环引用问题。 - 去重与排序:最终查询使用
DISTINCT去除重复记录,然后按RecordHierarchyLevel排序,完全符合预期结果要求。
结果验证
执行上述代码后,将得到与预期一致的层级排序结果,且能高效处理百万级数据集,避免了应用层递归的性能瓶颈。
内容的提问来源于stack exchange,提问作者Ravi Teli
相关产品推荐
相关产品推荐

