【问题标题】:SQL server - traverse tree in two directionsSQL server - 两个方向的遍历树
【发布时间】:2014-09-17 12:50:07
【问题描述】:

假设我有一个树状结构的区域表:

id     parent_id      name
---------------------------------
1      null           Europe
2      1              Germany
3      2              Köln
4      2              Berlin
5      1              Norway

现在我想管理树中任何节点的访问权限。访问权限与文档和用户有关。我想我可以使用递归 CTE 来获取给定节点处或下方的所有节点。例如,可以访问“德国”的用户应该可以访问与“德国”、“科隆”或“柏林”共享的所有文档,这很简单。

但是,当我想双向遍历树时,我迷路了。德国的用户还应该看到与“欧洲”共享的文档。 Köln 的用户应该能够看到“Köln”、“Germany”和“Europe”,但不能看到“Berlin”。

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

这个查询产生“Germany”、“Köln”、“Berlin”,从节点“Germany”开始上升,但我想运行一个查询,产生所有这些节点,加上任意数量的上升节点,但不是从“德国”分支出来。

我需要创建两个 CTE 并同时查询吗?

【问题讨论】:

    标签: sql sql-server tree common-table-expression


    【解决方案1】:

    您可以在 2 个CTE 结果集上执行UNION,该结果集在树的上下工作。

    所以 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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-11-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-09-29
      相关资源
      最近更新 更多