【问题标题】:How to count data in tree-like structure in SQL, for every node如何在 SQL 中以树状结构计算每个节点的数据
【发布时间】:2018-04-03 17:40:26
【问题描述】:

我有一棵这样的树:

我在表中有这个树层次结构。每个节点一行。 我想显示树中每个节点的子节点数。 结果是:

ID COUNT
100 9
129 5
439 3
450 1
501 2
602 1
134 3
133 2

表架构:

Table - Organization_structure
orgID
parentID

Table - Organization_detail
RID (PK)
OrganizationID
ParentOrganizationID

data:
orgID parrent ID
602   501
501   439
450   129
439   129
129   100
133   134
134   100

RID OrganizationID ParentOrganizationID
1   100            top
2   129            100
3   439            129
4   450            129
5   501            439
6   602            501
7   134            100
8   133            134
9   133            134

任何帮助如何实现这一目标?最好的 SQL 服务器。

【问题讨论】:

  • 请添加您的表架构。
  • @McNets 我添加表架构:)
  • 这两个表分别有什么数据?
  • 有两个ID=133
  • @McNets 是的,它们是两个,此外所有节点都有自己的 PK 女巫是 RID

标签: sql sql-server tree hierarchy


【解决方案1】:

这有点复杂,因为您应该使用递归查询,然后我使用了 CROSS APPLY 连接来计算记录:

我已经设置了下一个示例,(在您发布数据之前)

CREATE TABLE Organization_structure (orgID int, parentID int);

INSERT INTO Organization_structure VALUES
(100, NULL),
(129, 100),
(134, 100),
(439, 129),
(450, 129),
(133, 134),
(133, 134),
(501, 439),
(602, 501);

这是我的解决方案:

with tree as
(
    select orgId, parentId,0 as tree_order, path = cast('root' as varchar(100)) 
    from   Organization_structure 
    where  parentID is null
    union all
    select os.orgId, os.parentId, 1 + tree_order as tree_order,
           path = cast(tree.path + '/' + right(('000000000' + os.orgId), 10) as varchar(100))
    from   Organization_structure os
    join   tree 
    on     tree.orgId = os.parentId
)
select orgId, tree_order, path, t2.cnt 
from tree
cross apply (select count(*) cnt from tree t1 where t1.path like tree.path + '%') t2
order by tree_order;

结果如下:

组织ID |树序 |路径 | cnt ----: | ---------: | :-------------------- | --: 100 | 0 |根 | 9 129 | 1 |根/129 | 5 134 | 1 |根/134 | 3 133 | 2 |根/134/133 | 2 133 | 2 |根/134/133 | 2 第439章2 |根/129/439 | 3 450 | 2 |根/129/450 | 1 501 | 3 |根/129/439/501 | 2 602 | 4 |根/129/439/501/602 | 1

dbfiddle here

【讨论】:

  • 谢谢,看起来不错!如果我想按另一个属性排序怎么办,看看这个:link italic bold code 我添加了新列“类型”并且只想计算在此属性匹配的行之间,我希望看到类似 450 2 root/129/450 5 5 dusan 450 2 root/129/450 6 6 peter 的结果
  • 您也应该将此过滤器添加到交叉连接中,select sum(amount) cnt from tree t1 where t1.type = tree.type and t1.path like tree.path + '%'dbfiddle.uk/…
  • 如果数据中没有定义根怎么办?我的数据得到 0 行结果
  • 第一行是什么?我用过:where parentID is null
  • 我设法做到了。 @McNets 知道如何让它运行得更快吗?我有一堆不同的树,我正在应用这个计算。
【解决方案2】:

如果您将数据转换为hierarchyid,您可以使用IsDescendantOf 方法来获取这些数据。可能有更简单的方法,但这是我的第一个想法。第一部分只是为hierarchyid格式化数据。

计数魔法来自于我找到所有当前节点的后代节点。通过对orgid 进行分组,您可以获得所有后代的数量。

with data (OrgId, ParentOrgId) as
(
    select 100, null union all
    select 129, 100 union all
    select 134, 100 union all
    select 133, 134 union all
    select 135, 134 union all
    select 439, 129 union all
    select 450, 129 union all
    select 501, 439 union all
    select 602, 501 
), cte as
(
    select 
        sPath = cast(concat('/', OrgId, '/') as varchar(max)),
        Path = hierarchyid::Parse(concat('/', OrgId, '/')),
        PreviousPath = hierarchyid::GetRoot(),
        OrgId,
        ParentOrgId
    from data
    where ParentOrgId is null
    union all
    select 
        sPath = cast(concat(c.sPath, s.OrgId, '/') as varchar(max)),
        Path = hierarchyid::Parse(concat(c.sPath, s.OrgId, '/')),
        PreviousPath = c.Path,
        OrgId = s.OrgId,
        ParentOrgId = s.ParentOrgId
    from cte c
    inner join data s
        on c.OrgId = s.ParentOrgId
)
select a.OrgId, NumChildren = count(1)
from cte a
inner join cte b
    on b.Path.IsDescendantOf(a.Path) = 1
group by a.OrgId

您甚至可以在 CTE 中保留一个运行总数来检查后代,同时构建 hierarchyid 路径。

【讨论】:

  • 感谢您的回答,如果我没有定义什么是根节点怎么办?这里由值 (100, null) 定义,但我在数据中并不总是有这个值。
  • 我只通过没有父节点来识别根节点,而不是任何显式值。只要您的根节点定义类似,它就应该可以工作
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-11-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多