使用Cte访问递归树

时间:2019-08-24 12:29:12

标签: sql sql-server

我有3个数据表:有些人可以访问一个分支,而分支可以有一些子分支

餐桌人物

ID     PName
1       P1
2       P2

表分支

ID    Title    BrachIDRef
0      Master    null
1        B1       0 
2        B2       1
3        B3       2
4        B4       2
5        B5       1
6        B6       0
7        B7       6

表PersonBranches

Id  PersonIDref     BranchIDref
1   1                   1
2   2                   6
3   2                   2

我需要的结果是像这样在SQL Server查询中访问人员和分支和子分支:

P1  B1 
P1  (and All Childs of B1 - for each child have a record of data)
P2  B6
P2  and All Childs if B6
P2  B2
P2  and All Childs Of B2

2 个答案:

答案 0 :(得分:1)

您需要对Branchs表使用递归CTE,并且需要使用ROW_NUMBER()生成动态唯一记录ID,该ID将使用订单记录。

在UNION ALL选择查询之前,将生成具有记录ID的父记录值。 在UNION ALL选择查询之后,将基于父记录生成子记录。

请在下面的查询中查询您的预期结果。

SELECT * INTO #Persons
FROM (
SELECT '1' ID,'P1' PName UNION ALL
SELECT '2','P2') a

SELECT * INTO #Branches
FROM (        
SELECT '0' ID,'Master' Title,NULL BrachIDRef UNION ALL
SELECT '1','B1','0' UNION ALL  
SELECT '2','B2','1' UNION ALL
SELECT '3','B3','2' UNION ALL
SELECT '4','B4','2' UNION ALL
SELECT '5','B5','1' UNION ALL
SELECT '6','B6','0' UNION ALL
SELECT '7','B7','6'
) a

SELECT * INTO #PersonBranches
FROM (       
SELECT '1' Id,'1' PersonIDref,'1' BranchIDref UNION ALL
SELECT '2' Id,'2' PersonIDref,'6' BranchIDref UNION ALL
SELECT '3' Id,'2' PersonIDref,'2' BranchIDref
) a

;WITH branchCTE
AS
(
    SELECT 
        B.ID,B.Title,B.BrachIDRef,P.PName,ROW_NUMBER() OVER (ORDER BY B.BrachIDRef,B.ID) AS RID
    FROM #Branches B
    INNER JOIN #PersonBranches PB ON PB.BranchIDref = B.ID
    INNER JOIN #Persons P ON P.ID = PB.PersonIDref
    UNION ALL 
    SELECT
        B1.ID,B1.Title,B1.BrachIDRef,C.PName,C.RID
    FROM #Branches B1
    INNER JOIN branchCTE C ON C.ID = B1.BrachIDRef
    WHERE B1.BrachIDRef > 0
)
SELECT
    C.PName,
    C.Title
FROM branchCTE C
ORDER BY C.RID

DROP TABLE #Persons;
DROP TABLE #Branches;
DROP TABLE #PersonBranches;

答案 1 :(得分:-1)

您可以使用具有不同CTE的connect by before并将其与常规查询合并,以按以下方式获取所有子项:

cte as
(SELECT ANCESTOR,  
    LISTAGG(case when Title = ANCESTOR then null else title end, '/') within group (order by null) as pth from
    (SELECT DISTINCT ID,
        CONNECT_BY_ROOT Title as ANCESTOR, 
        Title
     FROM Branches
     CONNECT BY PRIOR ID =  BrachIDRef)
     GROUP BY
     ANCESTOR)
--
SELECT pb.id, p.pname, b.title
    from PersonBranches pb
        join persons p on (p.id = pb.PersonIDref) 
        join Branches b on (pb.BranchIDref = b.id)
union all
SELECT pb.id, p.pname, c.pth
    from PersonBranches pb
        join persons p on (p.id = pb.PersonIDref)   
        join Branches b on (pb.BranchIDref = b.id)
        join cte c on (b.title = c.ANCESTOR)
order by id;

db<>fiddle demo

干杯!