簡體   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