假设我有一个树状结构的区域表:
id parent_id name
---------------------------------
1 null Europe
2 1 Germany
3 2 Köln
4 2 Berlin
5 1 Norway
现在我想管理树中任何节点的访问权限。访问权限与文档和用户有关。我想我可以使用递归CTE来获取给定节点或其下的所有节点。例如,有权访问“德国”的用户应该可以访问与“德国”,“科隆”或“柏林”共享的所有文档,这很简单。
但是,当我想在两个方向上穿越树时,我迷失了方向。德国的用户还应该看到与“Europe”共享的文档。科隆的用户应该能够看到“Köln”,“德国”和“欧洲”,但不能看到“柏林”。
WITH RegionTree AS (
SELECT topRegion.Id RootId, topRegion.Name, topRegion.Id
FROM Region topRegion
UNION ALL
SELECT rt.RootId, r.Name, r.Id
FROM Region r
INNER JOIN RegionTree rt ON rt.Id = r.ParentId
)
SELECT rt.RootId, rt.Name, rt.Id
FROM RegionTree rt
WHERE rt.RootId = 2
此查询产生“德国”,“Köln”,“柏林”,从节点“德国”开始,但我想运行一个查询,产生所有这些节点,加上任意数量的节点,但不是从“德国”分支出来的。
我是否需要创建两个CTE并查询它们?
答案 0 :(得分:3)
您可以在2 UNION
结果集上执行CTE
,该结果集可在树上上下运行。
因此,CTE1中的连接将是:
INNER JOIN RegionTree rt ON rt.Id = r.ParentId
CTE2的加入将是反过来的另一种方式:
INNER JOIN RegionTree2 rt ON rt.ParentId = r.Id
因此在CTE结果上执行UNION
,(抱歉未经测试)
WITH RegionTree AS (
SELECT topRegion.Id RootId, topRegion.Name, topRegion.Id
FROM Region topRegion
UNION ALL
SELECT rt.RootId, r.Name, r.Id
FROM Region r
INNER JOIN RegionTree rt ON rt.Id = r.ParentId
),
RegionTree2 AS (
SELECT topRegion.Id RootId, topRegion.Name, topRegion.Id
FROM Region topRegion
UNION ALL
SELECT rt.RootId, r.Name, r.Id
FROM Region r
INNER JOIN RegionTree2 rt ON rt.ParentId = r.Id
)
SELECT rt.RootId, rt.Id, rt.Name, rt.CustomerId, rt.EntityId, rt.Depth
FROM RegionTree rt
WHERE rt.RootId = 2
UNION
SELECT rt.RootId, rt.Id, rt.Name, rt.CustomerId, rt.EntityId, rt.Depth
FROM RegionTree2 rt
WHERE rt.RootId = 2
虽然查看查询,但我可能会修改CTE以包含过滤条件而不是当前在WHERE
子句中的条件,以防止查询比所需更多的数据。像这样:
DECLARE @ID INT = 2
WITH RegionTree AS (
SELECT topRegion.Id RootId, topRegion.Name, topRegion.Id
FROM Region topRegion
WHERE topRegion.Id = @ID --ADDED
UNION ALL
SELECT rt.RootId, r.Name, r.Id
FROM Region r
INNER JOIN RegionTree rt ON rt.Id = r.ParentId
WHERE topRegion.Id IS NOT NULL -- ADDED
),
RegionTree2 AS (
SELECT topRegion.Id RootId, topRegion.Name, topRegion.Id
FROM Region topRegion
WHERE topRegion.Id = @ID --ADDED
UNION ALL
SELECT rt.RootId, r.Name, r.Id
FROM Region r
INNER JOIN RegionTree2 rt ON rt.ParentId = r.Id
WHERE topRegion.Id IS NOT NULL -- ADDED
)
SELECT rt.RootId, rt.Id, rt.Name, rt.CustomerId, rt.EntityId, rt.Depth
FROM RegionTree rt
UNION
SELECT rt.RootId, rt.Id, rt.Name, rt.CustomerId, rt.EntityId, rt.Depth
FROM RegionTree2 rt