sql - LAG 所有先前行 - 父子数据库查找每个父项的所有子项总数

标签 sql sql-server tree parent-child

我正在尝试从每个父级获取所有子级计数(子级计数)。

这是示例数据库 (MSSQL)。

INSERT INTO NODES VALUES(NULL, 1);
INSERT INTO NODES VALUES(NULL, 2);
INSERT INTO NODES VALUES(2, 3);
INSERT INTO NODES VALUES(1, 4);
INSERT INTO NODES VALUES(1, 5);
INSERT INTO NODES VALUES(3, 6);
INSERT INTO NODES VALUES(NULL, 7);
INSERT INTO NODES VALUES(NULL, 8);
INSERT INTO NODES VALUES(8, 9);
INSERT INTO NODES VALUES(7, 10);
INSERT INTO NODES VALUES(9, 11);
INSERT INTO NODES VALUES(11, 12);
INSERT INTO NODES VALUES(10, 13);
INSERT INTO NODES VALUES(10, 14);
INSERT INTO NODES VALUES(4, 15);

层次结构在哪里;

- 1
   - 4
      - 15
   - 5
- 2
   - 3
      - 6
- 7
   - 10
      - 13
      - 14
- 8
   - 9
      - 11
         - 12

期望的结果是:

<表类=“s-表”> <标题> id 子项计数 <正文> 1 3 4 1 5 0 2 2 3 1 6 0 7 3 10 2 13 1 14 0 8 3 9 2 11 1 12 0

每次我制定策略来制定查询时,我都会遇到必须在运行时迭代由查询形成的表的情况。

如果我对结果进行深入分组(使用parentid),我可以仅生成子项(而不是子项)的计数,以便我们可以将其加到根。但显然我需要迭代我在查询中形成的表,我不知道递归地说是否正确。

为了更好地表达自己,我会用我已经达到的部分和我想做的事情来展示;

WITH tree AS
(
    SELECT n1.parentid, n1.id, 1 AS level
    FROM NODES AS n1
    WHERE n1.parentid IS NULL
    UNION ALL
    SELECT n2.parentid, n2.id, level + 1 AS level
    FROM NODES AS n2
    INNER JOIN tree ON n2.parentid = tree.id
), levels AS 
(
  SELECT * 
  FROM tree
)
SELECT parentid, id, (COUNT(*) OVER(PARTITION BY parentid ORDER BY parentid)) AS childrencountofparentid, 
    ROW_NUMBER() OVER(ORDER BY parentid DESC) AS rownumber
FROM levels

输出是:

<表类=“s-表”> <标题> 父ID id childrencountofparentid 行号 <正文> 11 12 1 1 10 13 2 2 10 14 2 3 9 11 1 4 8 9 1 5 7 10 1 6 4 15 1 7 3 6 1 8 2 3 1 9 1 4 2 10 1 5 2 11 空 1 4 12 空 2 4 13 空 7 4 14 空 8 4 15

我想这样做: Actual query Full Image

我想使用前几行的结果,类似于滞后,但我必须迭代所有前几行。

最佳答案

我将使用 Hierarchyid 数据类型来解决这个问题。首先,使用递归 CTE 计算数据集中每一行的值。

with cte as (
    select ID, ParentID, 
       h = cast(concat('/', ID, '/') as varchar(100))
    from NODES
    where ParentID is null

    union all

    select child.ID, child.ParentID,
        h = cast(concat(Parent.h, child.ID, '/') as varchar(100))
    from NODES as child
    join cte as Parent
        on child.ParentID = Parent.ID
)
select ID, ParentID,
    h = cast(h as hierarchyid)
into #t
from cte;

这里唯一“棘手”的事情是我正在构建一个字符串,因为我正在遍历可以转换为hierarchyid数据类型的层次结构。

从那里,很容易计算相当于自连接的子项。

select ID, h.ToString(), count(child.a)
from #t as parent
outer apply (
    select a = 1
    from #t as child
    where child.h.IsDescendantOf(parent.h) = 1
        and child.ID <> Parent.ID
) as child
group by parent.ID, h
order by h;

注意 - 我仅在结果集中包含 h 列,以便 a) 显示返回父级的路径,b) 提供排序。如果您不需要这些,则无需包含。需要注意的是,在您想要的结果中,您的子数为 1,因此 13 为 1。我在数据(或您提供的可视化层次结构中)中没有看到 13 有任何子代。我确实看到它有一个 ID 为 14 的同级 - 如果需要计算它,则方法需要稍微改变。

这里需要注意的另一件事 - 如果您可以维护源数据中的 Hierarchyid 列(顺便说一下,这并不难做到),则根本不需要递归 CTE。我喜欢两种方法的混合。也就是说,将 ParentID 的概念保留为列,但在计算中使用 hierarchyid。如果 hierarchyid 被“损坏”(这是可能的,因为它实际上是从基础数据和应用程序逻辑派生的缓存/计算值),则可以根据 ID/ParentID 数据重新计算它。

关于sql - LAG 所有先前行 - 父子数据库查找每个父项的所有子项总数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/73220670/

相关文章:

sql-server - 如何使用 INSTEAD OF 触发器获取插入表中的新记录的标识

sql-server - 通过在T-SQL中设置种子从均匀分布生成随机值

c# - 目录树的倒数第二层

mysql - 修改前序树遍历,只检索n层深度?

算法回答 'possibility of building a triangle' 查询

mysql - ORM:仅当表B中存在id时才从表A中选择

java - Db2 表的 DDL 生成

java - 日期语句之间的 JPQL SELECT

sql - 通用 MERGE 的存储过程

sql-server - 为什么 T-SQL 不会因错误而停止