【问题标题】:How to write a CTE to aggregate hierarchical values如何编写 CTE 来聚合分层值
【发布时间】:2019-08-30 20:03:54
【问题描述】:

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

我有一个包含一些嵌套值的简单表“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

行可能有通过parent 字段引用其父级的子级。行可能具有自己的值以及子行,或者它们可能只是没有值的父行(即“包装器”)。叶子将是没有任何子元素的行。

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

我尝试了 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

查看最顶层的行,这并不完全正确,因为它不仅取行的直接子代的平均值,还取行的所有后代的平均值。例如,这会导致第 2 行的 total 为 62 而不是 60。预期结果应将第 2 行的总数设置为 60,因为它的直接子行 3 和 6 的平均值。第 1 行的总数也将是 60。

如何根据行值的平均值及其直接子项的值计算每行的“总”值,同时确保根据计算的总和正确填充层次结构的上层孩子们?

【问题讨论】:

  • 您的预期结果是什么?如果你只深入 1 级......为什么要使用递归查询?加入...
  • 这并不是绝对必须递归完成,而是在尝试简单的更新时,sqlite 采用了一种非常自上而下的方法,正如人们所期望的那样,导致总列的填充基于尚未计算的儿童总数。 PS。阐明了预期结果。
  • 那么为什么第 1 行的总数预计为 60。它是 NULL 值 + 子项(第 2 行的值为 null . NULL+60+Null 本身意味着 avg 是 20 对吗?或者因为我们有两个空值,所以它们不包含在 avg 中……我在这方面的数学/公式上苦苦挣扎。所以完整的预期结果你的例子会有所帮助。
  • 层次结构上层的总值应该基于它的直系子级已经正确计算和设置了他们的总数。这就是问题指定“自下而上”的原因。
  • 我现在明白了,这就是为什么递归查询现在有意义...

标签: sql sqlite common-table-expression


【解决方案1】:

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

How can I traverse a tree bottom-up to calculate a (weighted) average of node values in PostgreSQL?

由于 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

【讨论】:

    猜你喜欢
    • 2011-12-01
    • 2021-03-23
    • 2022-12-11
    • 2017-07-10
    • 2014-03-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-09
    • 1970-01-01
    相关资源
    最近更新 更多