SQL集合運算參考及案例(二):樹形節點數量逐級累計匯總


問題描述:

      我們經常遇到這樣一個問題,類似於面對一個樹形結構的物料數據,需要將庫存中每一種物料數量匯總到物料上展示出來;或者說組織機構是一棵樹,我們需要統計每一個節點上的人員數量(含下級節點的累計數量)。在此將解決的核心部分抽取出來。

      因為是樹形結構我們需要用到CTE的遞歸定義。CTE是一種十分優雅的存在,CTE所帶來最大的好處是代碼可讀性的提升,這是良好代碼的必須品質之一。使用遞歸CTE可以更加輕松愉快的用優雅簡潔的方式實現復雜的查詢。更重要的是標准的SQL是工作在DB關系運算引擎上,而游標等面向過程的代碼則不是,這會體現在運行效率上。

      在定義和使用遞歸CTE時應注意:遞歸 CTE 定義至少必須包含兩個 CTE 查詢定義,一個定位點成員和一個遞歸成員。可以定義多個定位點成員和遞歸成員;但必須將所有定位點成員查詢定義置於第一個遞歸成員定義之前。所有 CTE 查詢定義都是定位點成員,但它們引用 CTE 本身時除外。

 

注:最后一列是我們想要的值

Id

ParentId

Qty

Qty_Sum

1

0

  1

15

2

1

2

11

3

1

3

3

4

2

4

   9

5

4

  5

5

 

--- 構造測試數據的腳本

CREATE TABLE tMaterial
(
      Id        INT PRIMARY KEY
    , ParentId  INT
    , Qty       INT
    , Qty_Sum   INT
)

INSERT INTO tMaterial
            SELECT  1, 0, 1, 0
UNION ALL   SELECT  2, 1, 2, 0
UNION ALL   SELECT  3, 1, 3, 0
UNION ALL   SELECT  4, 2, 4, 0
UNION ALL   SELECT  5, 4, 5, 0
GO

   

傳統解答:使用自定義函數、遞歸、游標

CREATE FUNCTION fn_getQty_Sum(@Id INT)
RETURNS INT
AS
BEGIN
    DECLARE @Qty_Sum INT
    SELECT @Qty_Sum = Qty FROM tMaterial WHERE Id = @Id
    
    DECLARE   @OID  INT, @Qty INT
    DECLARE cursor1 CURSOR FOR 
        SELECT t.ID from tMaterial AS t WHERE t.ParentId = @Id
    OPEN cursor1
    
    FETCH NEXT FROM cursor1 INTO @OID
    
    WHILE @@FETCH_STATUS = 0 
    BEGIN
        SET @Qty = dbo.fn_getQty_Sum(@OID)
        SET @Qty_Sum = @Qty_Sum + @Qty
    
        FETCH NEXT FROM cursor1 INTO @OID
    END
    
    CLOSE cursor1
    DEALLOCATE cursor1

    RETURN @Qty_Sum
END

UPDATE tMaterial
SET Qty_Sum = dbo.fn_getQty_Sum(Id)

SELECT *
FROM tMaterial
 

 

推薦解答1:利用CTE的遞歸和樹形結構的特點,為樹形結構中的所有節點增加從根節點到當前節點的“訪問路徑”

WITH tmp AS 
(
    SELECT  t1.*, CAST(CAST(t1.Id AS NVARCHAR) + '.' AS NVARCHAR(100)) AS node_path
    FROM    tMaterial t1
    WHERE   t1.ParentId = 0
    UNION ALL 
    SELECT  t1.*, CAST(t2.node_path + CAST(t1.Id AS NVARCHAR) + '.' AS NVARCHAR(100))
    FROM    tMaterial t1
        JOIN tmp AS t2 ON t1.ParentId = t2.Id
)
, T2 AS 
(
    SELECT  t1.Id, t1.ParentId, t1.Qty, sum(t2.qty) AS Qty_Sum
    FROM    tmp t1
        JOIN tmp t2 ON t2.node_path LIKE t1.node_path + '%' 
    GROUP BY t1.Id, t1.ParentId, t1.Qty, t1.Qty_Sum
)

UPDATE T1
SET T1.Qty_Sum = T2.Qty_Sum
FROM tMaterial T1
    JOIN T2 ON T1.Id = T2.Id

SELECT * 
FROM tMaterial
 

 

推薦解答2:這個理解起來有點費勁,需要好好聯想一下遞歸定義與表關聯

WITH tmp AS (
    SELECT t.Id tm, * FROM tMaterial t
    UNION ALL
    SELECT t2.tm tm, t1.* FROM tMaterial t1 JOIN tmp t2 ON t1.ParentId = t2.Id
)
SELECT  tm,  sum(Qty) 
FROM    tmp 
GROUP BY tm
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM