简体   繁体   English

如何递归地求和 SQL 中的所有子树?

[英]How to SUM all subchildren tree in SQL recursively?

Good day, I've been pulling my hair on this problem for a while ><"美好的一天,我已经在这个问题上拉了一段时间的头发><"

I have 4 categories in a tree structure.我在树结构中有4 个类别

tenant_category_transaction_view:租户类别事务视图:

在此处输入图像描述

I would like to have the sum of all children "sumSubtotal" on every category我想在每个类别中获得所有孩子“sumSubtotal”的总和

Something like that:像这样的东西: 在此处输入图像描述

I've managed to come very close... But there's something I don't get ><"我已经非常接近了......但有些东西我没有得到><“

with recursive cte (sumSubtotal, sumQuantity, id, idParentCategory, treeSum, depth) as (

        select  root.sumSubtotal, -- STEP 1
                root.sumQuantity, 
                root.id, 
                root.idParentCategory, 
                root.sumSubtotal as treeSum,
                0 as depth
        from    tenant_category_transaction_view as root

        union all -- LOOP THROUGH ALL ROOT ROWS AND ADD ROWS TO THE CTE WITH THE INNER JOIN

        select  child.sumSubtotal, -- STEP 3
                child.sumQuantity, 
                child.id, 
                child.idParentCategory, 
                (cte.treeSum + child.sumSubtotal) AS treeSum,
                (cte.depth + 1) AS depth
        from    tenant_category_transaction_view AS child

        inner join cte on child.idParentCategory = cte.id -- STEP 2
)
select sumSubtotal, sumQuantity, id, idParentCategory, treeSum, depth -- STEP 4
from cte

Result of the above query:上述查询的结果:

在此处输入图像描述

It seems I'm generating the correct treeSum but upside down in only one branch看来我正在生成正确的 treeSum 但只在一个分支中颠倒

Would you be so kind to give me a hand?你会这么好心帮我一把吗?

Thank you for your time:)感谢您的时间:)

Try this one.试试这个。 First, we find all the branch lists recursively.首先,我们递归地找到所有的分支列表。 Each branch is identified by a corresponding root.每个分支都由相应的根标识。 Then, we aggregate the node quantities for each root/branch.然后,我们聚合每个根/分支的节点数量。

Note: The top of the tree shouldn't have a null id.注意:树的顶部不应有 null id。 That would violate the primary key constraint.这将违反主键约束。 The parent of the top node can be null, however.然而,顶层节点的父节点可以是 null。 I corrected that in my test case.我在我的测试用例中纠正了这一点。

Also, I could have carried the root row values (for each branch) through the recursion, avoiding the final join.此外,我可以通过递归携带根行值(对于每个分支),避免最终连接。

The fiddle 小提琴

WITH RECURSIVE cte AS (
        SELECT t.*, t.id AS root
          FROM tenant_category_transaction_view AS t
         UNION ALL
        SELECT t.*, t0.root
          FROM cte AS t0
          JOIN tenant_category_transaction_view AS t
            ON t.idParentCategory = t0.id
     )
SELECT root
     , MIN(t2.idParentCategory) AS idParentCategory
     , MIN(t2.sumSubtotal)      AS sumSubtotal
     , MIN(t2.sumQuantity)      AS sumQuantity
     , SUM(t1.sumSubtotal)      AS total
  FROM cte AS t1
  JOIN tenant_category_transaction_view AS t2
    ON t1.root = t2.id
 GROUP BY root
 ORDER BY root
;
root idParentCategory idParentCategory sumSubtotal总和小计 sumQuantity总数量 total全部的
1 1 null null 0 0 98 98 9890 9890
2 2 1 1 9800 9800 1 1 9800 9800
4 4 1 1 20 20 1 1 90 90
5 5 4 4 30 30 1 1 70 70
6 6 5 5 40 40 1 1 40 40

Additionally, this can be written to aggregate based on t2.id, which is the primary key, allowing slight simplification, due to functional dependence.此外,这可以写入基于 t2.id 的聚合,这是主键,由于功能依赖性,允许稍微简化。

WITH RECURSIVE cte AS (
        SELECT t.*, t.id AS root
          FROM tenant_category_transaction_view AS t
         UNION ALL
        SELECT t.*, t0.root
          FROM cte AS t0
          JOIN tenant_category_transaction_view AS t
            ON t.idParentCategory = t0.id
     )
SELECT t2.id
     , t2.idParentCategory
     , t2.sumSubtotal
     , t2.sumQuantity
     , SUM(t1.sumSubtotal)      AS total
  FROM cte AS t1
  JOIN tenant_category_transaction_view AS t2
    ON t1.root = t2.id
 GROUP BY t2.id
 ORDER BY t2.id
;

Finally, we can remove the last JOIN by carrying other root values within the recursive logic:最后,我们可以通过在递归逻辑中携带其他根值来删除最后一个 JOIN:

WITH RECURSIVE cte AS (
        SELECT t.*, t.id AS root
             , idParentCategory AS idParentCategory0
             , sumSubtotal      AS sumSubtotal0
             , sumQuantity      AS sumQuantity0
          FROM tenant_category_transaction_view AS t
         UNION ALL
        SELECT t.* , t0.root
             , t0.idParentCategory0
             , t0.sumSubtotal0
             , t0.sumQuantity0
          FROM cte AS t0
          JOIN tenant_category_transaction_view AS t
            ON t.idParentCategory = t0.id
     )
SELECT root
     , MIN(idParentCategory0)   AS idParentCategory
     , MIN(sumSubtotal0)        AS sumSubtotal
     , MIN(sumQuantity0)        AS sumQuantity
     , SUM(t1.sumSubtotal)      AS total
  FROM cte AS t1
 GROUP BY root
 ORDER BY root
;

The setup:设置:

CREATE TABLE tenant_category_transaction_view (
    id               int primary key
  , idParentCategory int
  , sumSubtotal      int
  , sumQuantity      int
);

INSERT INTO tenant_category_transaction_view VALUES
    (1, null,    0,  0)
  , (2,    1, 9800, 98)
  , (4,    1,   20,  1)
  , (5,    4,   30,  1)
  , (6,    5,   40,  1)
;

The solution is actually quite simple:-)解决方案实际上很简单:-)

  1. Inside the CTE add an hard-coded row that represents the root node (where id is NULL).在 CTE 中添加一个表示根节点的硬编码行(其中 id 为 NULL)。 Please not that we can use multiple anchore queries within a CTE (queries that does not refer to the name of the CTE).请注意,我们可以在 CTE 中使用多个锚定查询(不引用 CTE 名称的查询)。
  2. Create all paths from all node to all of their descendants (including themselves).创建从所有节点到其所有后代(包括它们自己)的所有路径。 Each path is represented by a row that holds the start node id, the descendant node id and the amount of the descendant node.每条路径由一行表示,其中包含起始节点 id、后代节点 id 和后代节点的数量。 Please note the use of the null-safe equal operator ( <=> )请注意使用空安全相等运算符 ( <=> )
  3. Aggregate by the starting nodes ids and for each node summarize the amount of all of its descendants按起始节点 id 聚合,并为每个节点汇总其所有后代的数量

PS附言
There is nothing recursive about "recursive" queries. “递归”查询没有递归。 It is a misleading name.这是一个误导性的名称。 Those are actually iterative queries, not recursive.这些实际上是迭代查询,而不是递归的。


with recursive cte (root_id, id, sumSubtotal)  as 
(
  select     null
            ,null
            ,0
  
  union all
  
  select     id
            ,id
            ,sumSubtotal

  from      tenant_category_transaction_view as tctv
  
  union all
  
  select     cte.root_id
            ,tctv.id
            ,tctv.sumSubtotal

  from      cte 
            join    tenant_category_transaction_view as tctv 
            on      tctv.idParentCategory <=> cte.id
)
select   root_id
        ,sum(sumSubtotal)
from     cte
group by root_id

+---------+-------------+
| root_id | sumSubtotal |
+---------+-------------+
| null    |        9890 |
| 1       |        9800 |
| 4       |          90 |
| 5       |          70 |
| 6       |          40 |
+---------+-------------+

fiddle 小提琴

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM