等级和

时间:2012-09-11 08:17:42

标签: sql sql-server

我有这种结构的表格:

Id ParentId CountItems

我可以通过脚本计算项目的级别:

 ;WITH cteSort AS
  (
  SELECT Id AS Child, ParentId AS Parent, 1  AS [Level]
    FROM [Catalog].[Category] WHERE Id = 0 
  union ALL
  SELECT Id AS Child, ParentId AS Parent, [Level] + 1 AS [Level]
  FROM [Catalog].[Category] 
       INNER JOIN cteSort ON [Category].ParentId = cteSort.Child and Id <>0)

我希望从较低级别上升到顶部并将当前CountItems放在较低级别的CountItems子项和当前CountItems之间。 像这样: 前

Id ParentId count 
0     0      0 
1     0      1 
2     0      1 
3     1      1 
4     1      1 
5     2      1 
6     3      1 
7     4      1 
8     4      1 

Id ParentId count
0    0        8 
1    0        6 
2    0        2  
3    1        2 
4    1        3 
5    2        1 
6    3        1 
7    4        1 
8    4        1 

3 个答案:

答案 0 :(得分:1)

我最近在关系演示文稿中遇到了树问题,所以我尝试解决您的问题但是在重试之后我得到以下输出:GROUP BY, HAVING, or aggregate functions are not allowed in the recursive part of a recursive common table expression写下来时

WITH sum_cte AS
(
    SELECT
       Id, ParentId, [Count]
    FROM Category c

    -- find Categories that have no child
    WHERE NOT EXISTS(SELECT 1 FROM Category WHERE ParentId = c.Id)

    UNION ALL


    SELECT
        s.ParentId
        -- find parent of current parent
        , c.ParentId
        , SUM(s.[Count])
    FROM 
        sum_cte s  
        JOIN Category c 
        ON s.ParentId = c.Id
    WHERE s.Id<>0
    GROUP BY s.ParentId, c.ParentId
)

SELECT DISTINCT * FROM sum_cte ORDER BY Id 

我认为用CTE解决问题是不可能的。所以我写了一个递归函数,它给出了所有孩子的总数。

CREATE FUNCTION [dbo].[GetSumOfChildren](@CategoryId INT)
RETURNS INT
AS
BEGIN
    DECLARE @sum int;
    SELECT 
        -- get sum of all children
        @sum = SUM(dbo.GetSumOfChildren(Id))
    FROM
        Category
    WHERE ParentId = @CategoryId AND ParentId <> Id

    SELECT 
        -- plus self count
        @sum = ISNULL(@sum,0) + [Count]
    FROM
        Category
    WHERE Id = @CategoryId

    RETURN @sum;
END

这样你的任务解决了简单的问题

SELECT Id,ParentId, dbo.GetSumOfChildren(Id) FROM Category

答案 1 :(得分:1)

我自己的解决方案,将新字段插入表[level]:

;WITH cteSort AS
  (
  SELECT Id AS Child, ParentId  AS Parent,1  AS [Level]
    FROM table WHERE Id = 0 
  union ALL
  SELECT 
     Id AS Child,
     ParentId AS Parent,
     cteSort.[Level] + 1 AS [Level]
  FROM table 
       INNER JOIN cteSort ON table.ParentId = cteSort.Child and table.Id<>0)

UPDATE table SET [Level] = (Select [Level] from cteSort where cteSort.Child = table.Id)

declare @max int
set @max = (select top(1) [Level] from table order by [Level]  Desc)
 while (@max >0)
 begin
     UPDATE t1
        SET t1.[count] = t1.[count] + t2.cnt ,
        from table as t1 
        cross apply (Select COALESCE(SUM([count]),0) cnt from table where ParentId =t1.Id) t2
        where t1.Id in (Select Id from table where [Level] =@max)
     Set @max = @max -1
 end

答案 2 :(得分:0)

我猜你的问题更多地导致了原因?而不是一个实际的解决方案(在我的午餐期间对此感到困惑)我确实有一些贡献。

在纯编程术语中,您可以使用递归函数来完成此操作,该函数通过父计数和要处理的节点,同时保持当前叶的内部计数。在纯SQL中进行仿真将是一个更有趣的尝试,因为在SQL中使用的一些编程技术更难实现(如果有的话)。

我的实际想法让我不是每行使用som痉挛子查询(根据行数会很昂贵,并且可能需要固定数量的级别)但是将结果集转换为XML对象然后然后使用XQuery获取后代的数量,然后使用结果更新结果集。不幸的是count()没有开关来计算所有后代,所以你必须恢复到一些子查询类型逻辑来逐行计数。此外,这种方法似乎指向具有固定数量的级别......

这让它回归原因?如果将此结果集传递给C#,Java等编程语言以进行处理,那么在此时实现递归计算并在使用之前将其添加到结果集会更有意义。