递归SUM Sql Server

时间:2012-07-10 07:52:53

标签: sql-server sum common-table-expression

我需要在SQL Server中进行递归求和。我想要一个存储过程,我可以在其中传递父ID,然后返回与该父ID相关联的所有子项(和子项的子项)的总计。

这是我到目前为止所拥有的

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 个答案:

答案 0 :(得分: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为null的行设置为开始时完成,因此将首先计算它们的直接后代,然后计算这些行的后代等等。

答案 1 :(得分: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