如何编写 CTE 来聚合分层值

How to write a CTE to aggregate hierarchical values

我想在 sqlite 中编写表达式来处理项目树,从叶节点(底部)开始,然后返回到它们的 parents 一直到根节点(顶部) ,这样每个 parent 节点都会根据其 children 的内容进行更新。我已经能够编写一个 CTE 来做类似的事情,但还不完全正确。

我有一个简单的 table "test1" 包含一些嵌套值:

id | parent | value | total
---+--------+--------------
1  | NULL   | NULL  | NULL
2  | 1      | NULL  | NULL
3  | 2      | NULL  | NULL
4  | 3      | 50    | NULL
5  | 3      | 50    | NULL
6  | 2      | 60    | NULL
7  | 6      | 90    | NULL
8  | 6      | 60    | NULL

行可能有 child人通过 parent 字段引用他们的 parent。行可能有它们自己的值以及 child 行,或者它们可能只是 parent 没有值(即 "wrappers")。叶子将是没有任何 children.

的行

对于每一行,我想计算 total,作为平均值或该行的 value(如果不为空)及其 children 的 total秒。这应该从叶节点开始,然后沿着树向上移动到它们的 parents,一直到数据层次结构顶部的根节点。

我已经尝试了多种 CTE 的变体,但我很难编写一个能够自下而上递归计算这些总数的变体。

目前,我有:

UPDATE test1 SET total = (
  WITH RECURSIVE cte(cte_id,cte_parent,cte_value,cte_total) AS (
    SELECT test1.id, test1.parent, test1.value, test1.total
      UNION ALL
    select t.id, t.parent, t.value, t.total from test1 t, cte
    WHERE cte.cte_id=t.parent
  ) SELECT AVG(cte_value) FROM cte
);

产生:

id | parent | value | total
---+--------+-------+------
1  | NULL   | NULL  | 62
2  | 1      | NULL  | 62
3  | 2      | NULL  | 50
4  | 3      | 50    | 50
5  | 3      | 50    | 50
6  | 2      | 60    | 70
7  | 6      | 90    | 90
8  | 6      | 60    | 60

查看 top-most 行,这不太正确,因为它不仅取该行直接 children 的平均值,而且取 all[=37] 的平均值=] 该行的后代。例如,这会导致第 2 行的 total 为 62 而不是 60。预期结果应将第 2 行的总数设置为 60,作为其直接 child 第 3 行和第 6 行的平均值。第 1 行的总数也将是 60。

我如何根据行值的平均值及其直接 children 的值计算每行的 "total" 值,同时确保层次结构的上层是根据 children?

的计算总数正确填充

原来在这里发布了一个非常相似的问题和解决方案:

由于 sqlite3 不允许您创建函数,因此适用使用递归 CTE 的示例:

with recursive cte(id, parent, value, level, total) as (
    select
        t.id, t.parent, t.value,
        0,
        t.value as total
    from test1 t
    where not exists (
        select id
        from test1
        where parent = t.id)
union all
    select
        t.id, t.parent, t.value,
        c.level+1,
        case when t.value is null then c.total else t.value end
    from test1 t
    join cte c on t.id=c.parent
)
select id, parent, value, avg(total) total from (
    select
        id, parent, value, level, avg(total) total
        from cte
        group by id,parent,level
)
group by id, parent
order by id