繁体   English   中英

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

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

美好的一天,我已经在这个问题上拉了一段时间的头发><"

我在树结构中有4 个类别

租户类别事务视图:

在此处输入图像描述

我想在每个类别中获得所有孩子“sumSubtotal”的总和

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

我已经非常接近了......但有些东西我没有得到><“

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

上述查询的结果:

在此处输入图像描述

看来我正在生成正确的 treeSum 但只在一个分支中颠倒

你会这么好心帮我一把吗?

感谢您的时间:)

试试这个。 首先,我们递归地找到所有的分支列表。 每个分支都由相应的根标识。 然后,我们聚合每个根/分支的节点数量。

注意:树的顶部不应有 null id。 这将违反主键约束。 然而,顶层节点的父节点可以是 null。 我在我的测试用例中纠正了这一点。

此外,我可以通过递归携带根行值(对于每个分支),避免最终连接。

小提琴

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
;
idParentCategory 总和小计 总数量 全部的
1 null 0 98 9890
2 1 9800 1 9800
4 1 20 1 90
5 4 30 1 70
6 5 40 1 40

此外,这可以写入基于 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
;

最后,我们可以通过在递归逻辑中携带其他根值来删除最后一个 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
;

设置:

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)
;

解决方案实际上很简单:-)

  1. 在 CTE 中添加一个表示根节点的硬编码行(其中 id 为 NULL)。 请注意,我们可以在 CTE 中使用多个锚定查询(不引用 CTE 名称的查询)。
  2. 创建从所有节点到其所有后代(包括它们自己)的所有路径。 每条路径由一行表示,其中包含起始节点 id、后代节点 id 和后代节点的数量。 请注意使用空安全相等运算符 ( <=> )
  3. 按起始节点 id 聚合,并为每个节点汇总其所有后代的数量

附言
“递归”查询没有递归。 这是一个误导性的名称。 这些实际上是迭代查询,而不是递归的。


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 |
+---------+-------------+

小提琴

暂无
暂无

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

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