2012-07-10 116 views
5

我需要在SQL Server中進行遞歸求和。我想要一個存儲過程,我可以傳入一個父ID,然後返回所有與該父ID相鏈接的子(和子的孩子)的總數。遞歸SUM Sql服務器

這裏是我迄今爲止

IF object_id('tempdb..#Averages') IS NOT NULL 
BEGIN 
    DROP TABLE #Averages 
END 


CREATE TABLE #Averages 
(
ID INT PRIMARY KEY CLUSTERED IDENTITY(1,1), 
Name VARCHAR(255), 
ParentID int, 
Value INT 
) 

INSERT INTO #Averages(Name,ParentID,Value)VALUES('Fred',NULL,1) 
INSERT INTO #Averages(Name,ParentID,Value)VALUES('Bets',NULL,1) 

INSERT INTO #Averages(Name,ParentID,Value)(SELECT 'Wynand',ID,21 FROM #Averages WHERE  Name = 'Fred' ) 

INSERT INTO #Averages(Name,ParentID,Value)(SELECT 'Dewald',ID,27 FROM #Averages WHERE  Name = 'Fred' ) 
INSERT INTO #Averages(Name,ParentID,Value)(SELECT 'Katelynn',ID,1 FROM #Averages WHERE Name = 'Dewald' ) 

INSERT INTO #Averages(Name,ParentID,Value)(SELECT 'Jacques',ID,28 FROM #Averages WHERE Name = 'Bets' ) 
INSERT INTO #Averages(Name,ParentID,Value)(SELECT 'Luan',ID,4 FROM #Averages WHERE Name = 'Jacques' ) 
INSERT INTO #Averages(Name,ParentID,Value)(SELECT 'Ruben',ID,2 FROM #Averages WHERE Name = 'Jacques' ) 


;WITH Personal AS 
(
SELECT N=1, ID,Name,ParentID,Value 
FROM #Averages 
WHERE ParentID IS NULL 
UNION ALL 
SELECT N+1, Av.ID,Av.Name,Av.ParentID,Av.Value 
FROM #Averages Av 
INNER JOIN Personal P ON P.ID = Av.ParentID 
) 

SELECT Name, 
    SUM(Value) as Total 
FROM Personal 
WHERE N<=3 
GROUP BY Name 
+2

@alfasin - 不確定爲什麼你不斷提及標記爲SQL Server的問題中的MySQL? – 2012-07-10 08:05:53

+0

@MartinSmith太晚了...太累了...... :)刪除評論爲非建設性 – alfasin 2012-07-10 08:09:17

+0

肯定是一個錯字。 sory :-) – Captain0 2012-07-10 08:18:46

回答

2

玩了一下後,我想我明白了。我添加了一個頂級ID,這是我在CTE的Root中設置的。然後,只需爲所有遞歸添加頂級ID。

最後我只求和,基本上用TopLevelId加入頂層表。

IF object_id('tempdb..#Averages') IS NOT NULL 
BEGIN 
    DROP TABLE #Averages 
END 

CREATE TABLE #Averages 
(
    ID INT PRIMARY KEY CLUSTERED IDENTITY(1,1), 
    Name VARCHAR(255), 
    ParentID int, 
    Value INT 
) 

INSERT INTO #Averages(Name,ParentID,Value)VALUES('Fred',NULL,1) 
INSERT INTO #Averages(Name,ParentID,Value)VALUES('Bets',NULL,1) 

INSERT INTO #Averages(Name,ParentID,Value)(SELECT 'Wynand',ID,21 FROM #Averages WHERE Name = 'Fred' ) 

INSERT INTO #Averages(Name,ParentID,Value)(SELECT 'Dewald',ID,27 FROM #Averages WHERE Name = 'Fred' ) 
INSERT INTO #Averages(Name,ParentID,Value)(SELECT 'Katelynn',ID,1 FROM #Averages WHERE Name = 'Dewald' ) 

INSERT INTO #Averages(Name,ParentID,Value)(SELECT 'Jacques',ID,28 FROM #Averages WHERE Name = 'Bets' ) 
INSERT INTO #Averages(Name,ParentID,Value)(SELECT 'Luan',ID,4 FROM #Averages WHERE Name = 'Jacques' ) 
INSERT INTO #Averages(Name,ParentID,Value)(SELECT 'Ruben',ID,2 FROM #Averages WHERE Name = 'Jacques' ) 


;WITH Personal AS 
(
    SELECT N=1, 
     ID, 
     Name, 
     ParentID, 
     Value, 
     TopLevelID =ID 
    FROM #Averages 
    WHERE ParentID IS NULL 

    UNION ALL 

    SELECT N+1, 
     Av.ID, 
     Av.Name, 
     Av.ParentID, 
     Av.Value, 
     TopLevelID =P.TopLevelID 
    FROM #Averages Av 
    INNER JOIN Personal P ON P.ID = Av.ParentID 
) 

SELECT SUM(P.Value) AS Total, 
     A.Name 
FROM Personal P 
INNER JOIN #Averages A on A.ID = P.TopLevelID 
GROUP BY A.Name 
+0

非常好和清楚的答案,其中「個人」是微軟例子中的CTE表。謝謝! – 2016-03-09 18:54:37

3

這裏實現你想要的東西的一種方式,儘管它的做法略有不同,你有以上:

SQLFiddle

Create Table #Ancestors (
    ID int 
    , Name VARCHAR(255) 
    , ParentID int 
    , AncestryCompleteTF tinyint 
    , Ancestors varchar(max) 
    , TotalValue int  
) 

INSERT INTO #Ancestors 
SELECT 
    ID 
    , Name 
    , ParentID 
    , CASE ISNULL(ParentID, 0) 
    WHEN 0 THEN 1 
    ELSE 0 
    END 
    , CONVERT(VARCHAR, ISNULL(ParentID, '')) 
    , Value 
FROM 
    Averages 

WHILE EXISTS (SELECT * FROM #Ancestors WHERE AncestryCompleteTF = 0) 
BEGIN 
    UPDATE C SET 
    C.Ancestors = P.Ancestors + ',' + CONVERT(VARCHAR, P.ID), 
    C.AncestryCompleteTF = 1, 
    C.TotalValue = P.TotalValue + C.TotalValue 
    FROM #Ancestors C 
    INNER JOIN #Ancestors P ON (C.ParentID = P.ID) 
    AND P.AncestryCompleteTF = 1 
END 

SELECT 
    Name 
, TotalValue 
FROM 
    #Ancestors 

基本上我創建了一個臨時表,並使用一個while循環來更新已經計算出父代的行的總數(因爲這僅僅是一個廣告的例子將當前行的總數改爲父行的總數),直到所有行都已計算完畢。 ParentID爲空的行被設置爲開頭,所以他們的直接後代將首先計算,然後這些行的後代等。等等。

+1

小提琴很適合創造人們圍繞問題玩的樣本,但通常最好至少有實際的解決方案查詢(如果不是架構/數據設置)在你的答案中。 – 2012-07-10 08:37:48

+0

@Damien_The_Unbeliever - 同意。現在粘貼進來。 – soupy1976 2012-07-10 08:45:27