SQL - 在数据层次结构/树中选择父/顶节点和子节点

时间:2011-08-18 01:05:21

标签: sql tree hierarchy

我正在处理以前没有遇到过的数据结构。该表是树层次结构,看起来像这样:

CREATE TABLE Tree (
    TreeID INT, -- Primary key id
    TreeLevel INT, -- Level in the tree
    TreeDown INT, -- TreeID of first node at the next lower level
    TreeRight INT -- TreeID of the next node at the same level with 0 being the end
)

然后,我有一个项目列表,这些项目通过TreeID链接到层次结构中的各个节点。 E.g:

CREATE TABLE Items (
    ItemID INT,
    TreeID INT, -- Node in the tree hierarchy
    Value INT
)

我想在树的顶层做一个选择和分组,所以包括所有的孩子。例如:

SELECT Tree.TreeID, SUM(Items.Value) FROM Items
JOIN Tree ON Tree.TreeID = Items.TreeID AND Tree.TreeLevel = 0
GROUP BY Tree.TreeID

当然,这实际上并不起作用,只是以此为例。实际上,这只包括显式分配给碰巧具有0级但不是其子级的树节点的项。我如何包括他们的孩子?

如果重要的话,我正在使用SQL Server 2008,但是不可知的解决方案会很棒。

2 个答案:

答案 0 :(得分:1)

看看这个相关的问题。你需要创建一个recursive-cte,这有类似的问题/解决方案。

CTE to traverse back up a hierarchy?

答案 1 :(得分:0)

TreeLevel 对于解决方案来说是不必要的,所以我将其排除在外。

样本数据

create table Tree 
(
    TreeID    int -- Primary key id
   ,TreeDown  int -- TreeID of first node at the next lower level
   ,TreeRight int -- TreeID of the next node at the same level with 0 being the end
)
;

insert into Tree (TreeID,TreeDown,TreeRight) values (1,2,0);
insert into Tree (TreeID,TreeDown,TreeRight) values (2,4,3);
insert into Tree (TreeID,TreeDown,TreeRight) values (3,7,0);
insert into Tree (TreeID,TreeDown,TreeRight) values (4,0,5);
insert into Tree (TreeID,TreeDown,TreeRight) values (5,0,6);
insert into Tree (TreeID,TreeDown,TreeRight) values (6,0,0);
insert into Tree (TreeID,TreeDown,TreeRight) values (7,8,0);
insert into Tree (TreeID,TreeDown,TreeRight) values (8,0,9);
insert into Tree (TreeID,TreeDown,TreeRight) values (9,0,0);

insert into Tree (TreeID,TreeDown,TreeRight) values (10,11,0);
insert into Tree (TreeID,TreeDown,TreeRight) values (11,0,0);

insert into Tree (TreeID,TreeDown,TreeRight) values (12,13,0);
insert into Tree (TreeID,TreeDown,TreeRight) values (13,0,14);
insert into Tree (TreeID,TreeDown,TreeRight) values (14,15,0);
insert into Tree (TreeID,TreeDown,TreeRight) values (15,16,0);
insert into Tree (TreeID,TreeDown,TreeRight) values (16,0,0);


create table Items 
(
    ItemID  int
   ,TreeID  int -- Node in the tree hierarchy
   ,Val     int
)
;

insert into Items (ItemID,TreeID,Val) values (1001,1,1);
insert into Items (ItemID,TreeID,Val) values (1002,1,1);
insert into Items (ItemID,TreeID,Val) values (1004,6,2);
insert into Items (ItemID,TreeID,Val) values (1005,7,2);
insert into Items (ItemID,TreeID,Val) values (1006,8,0);
insert into Items (ItemID,TreeID,Val) values (1007,9,3);
insert into Items (ItemID,TreeID,Val) values (1008,12,4);
insert into Items (ItemID,TreeID,Val) values (1009,15,3);
insert into Items (ItemID,TreeID,Val) values (1010,15,2);
insert into Items (ItemID,TreeID,Val) values (1011,15,1);

解决方案

Oracle / SQL Server / SQLLite

对于PostgreSQL / Teradata,添加“递归”字样。在'之后用'

with        cte (root_TreeId,TreeId,TreeDown,TreeRight)
            as
            (
                    select      t.TreeId        as root_TreeId
                               ,t.TreeId
                               ,t.TreeDown
                               ,t.TreeRight

                    from        Tree    t   

                    where       t.TreeId not in (select TreeDown from Tree union select TreeRight from Tree)

                    union all

                    select      c.root_TreeId
                               ,t.TreeID
                               ,t.TreeDown
                               ,t.TreeRight

                    from                    cte     c

                                join        Tree    t

                                on          t.TreeID = c.TreeDown
                                        or  t.TreeID = c.TreeRight
            )

select      c.root_TreeID

           ,count (distinct c.TreeID)   as nodes
           ,count (i.ItemID)            as items
           ,sum   (i.Val)               as sum_item_value

from                    cte     c

            left join   Items   i

            on          i.TreeID    =
                        c.TreeID

group by    c.root_TreeID
;