代码之家  ›  专栏  ›  技术社区  ›  Jrgns

如何使用SQL计算树中的值之和

  •  7
  • Jrgns  · 技术社区  · 16 年前

    我需要对用户树所获得的每个级别的点数进行相加。级别1是低于用户级别1的用户的积分之和。二级是用户的一级点,低于用户二级等。。。

    SQL看起来像什么?

    如果你困惑了,别担心,我也是!

    用户表:

    ID    ParentID    Points
    1     0           230
    2     1           150
    3     0           80
    4     1           110
    5     4           54
    6     4           342
    
    Tree:
    0
    |---\
    1    3
    | \
    2  4---
        \  \
         5  6
    

    输出应为:

    ID    Points    Level1     Level2
    1     230       150+110    150+110+54+342
    2     150
    3     80
    4     110       54+342
    5     54
    6     342
    

    最好是SQL Server语法和函数。。。

    9 回复  |  直到 16 年前
        1
  •  2
  •   Manrico Corazzi    16 年前

    如果您使用的是Oracle DBMS,这将非常简单,因为Oracle支持使用 语法。对于SQL Server,我想您可能会发现 Common Table Expressions 有用的

        2
  •  2
  •   C B dkretz    7 年前

    树不能很好地与SQL一起工作。如果您有非常(非常)少的写访问,您可以将树实现更改为使用嵌套集,这将使此查询非常容易。

    SELECT SUM(points) 
    FROM users 
    where left > x and right < y 
    

    但是,树上的任何更改都需要接触大量行。最好在客户端中进行递归。

        3
  •  1
  •   Grad van Horck    16 年前

    或者如果您有最大数量的级别,您可以创建子查询,但它们的性能非常差。

    (或者您可以获取mssqlserver2008并获取新的层次结构函数。。。;) )

        4
  •  1
  •   tzot    16 年前

    像其他人所说的,SQL通常不能很好地处理这种关系。通常,需要一个代理项“relations”表(id,parent_id,unique key on(id,parent_id)),其中:

    • INSERT INTO relations (id, parent_id) VALUES ([current_id], [current_id]);

      INSERT INTO relations (id, parent_id) VALUES ([current_id], [current_parent_id]);

      INSERT INTO relations (id, parent_id) SELECT [current_id], parent_id FROM relations WHERE id = [current_parent_id];

    • 有逻辑避免循环

    • 确保用存储过程处理“relations”上的更新和删除

    有了那张桌子,你需要:

    SELECT rel.parent_id, SUM(tbl.points)
    FROM table tbl INNER JOIN relations rel ON tbl.id=rel.id
    WHERE rel.parent_id <> 0
    GROUP BY rel.parent_id;
    
        5
  •  1
  •   Darrel Miller    16 年前

    好吧,这给了你你想要的结果,但不能保证我没有错过什么。把它当作一个起点。我用了SQL 2005,SQL 2000不支持CTE

    WITH Parent (id, GrandParentId, parentId, Points, Level1Points, Level2Points)
    AS
    (
        -- Find root
        SELECT id,  
                0 AS GrandParentId,
                ParentId,
                Points,
                0 AS Level1Points,
                0 AS Level2Points
        FROM tblPoints ptr
        WHERE ptr.ParentId = 0
    
        UNION ALL (
        -- Level2 Points
        SELECT pa.GrandParentId AS Id,
                NULL AS GrandParentId,
                NULL AS ParentId,
                0 AS Points, 
                0 AS Level1Points,
                pa.Points  AS Level2Points
        FROM tblPoints pt
                JOIN Parent pa ON pa.GrandParentId = pt.Id 
        UNION  ALL
        -- Level1 Points
        SELECT pt.ParentId AS Id,
                NULL AS GrandParentId,
                NULL AS ParentId,
                0 AS Points, 
                pt.Points AS Level1Points,
                0 AS Level2Points
        FROM tblPoints pt
                JOIN Parent pa ON pa.Id = pt.ParentId AND pa.ParentId IS NOT NULL 
        UNION  ALL
        -- Points
        SELECT pt.id,
                pa.ParentId AS GrandParentId,
                pt.ParentId,
                pt.Points, 
                0 AS Level1Points,
                0 AS Level2Points
        FROM tblPoints pt
                JOIN Parent pa ON pa.Id = pt.ParentId AND pa.ParentId IS NOT NULL )
    )
    SELECT id, 
        SUM(Points) AS Points,  
        SUM(Level1Points) AS Level1Points,
        CASE WHEN SUM(Level2Points) > 0 THEN  SUM(Level1Points) + SUM(Level2Points) ELSE 0 END AS Level2Points
    FROM Parent
    GROUP BY id 
    ORDER by id
    
        6
  •  1
  •   C B dkretz    7 年前

    如果您正在处理存储在关系数据库中的树,我建议您查看“嵌套集”或“修改的预排序树遍历”。SQL将非常简单:

    SELECT id, 
           SUM(value) AS value 
    FROM table 
    WHERE left>left\_value\_of\_your\_node 
      AND right<$right\_value\_of\_your\_node;
    

    ... 对你感兴趣的每个节点都这样做。

    也许这能帮你: http://www.dbazine.com/oracle/or-articles/tropashko4 或者使用谷歌。

        7
  •  0
  •   Ilya Kochetov    16 年前

    1. 使用游标和递归的用户定义函数调用(非常慢)
    2. 执行客户端递归计算(如果没有太多记录,则更可取)
        8
  •  0
  •   Milan BabuÅ¡kov    16 年前

    CREATE FUNCTION CALC
    (
    @node integer,
    )
    returns 
    (
    @total integer
    )
    as
    begin
        select @total = (select node_value from yourtable where node_id = @node);
    
        declare @children table (value integer);
        insert into @children   
        select calc(node_id) from yourtable where parent_id = @node;
    
        @current = @current + select sum(value) from @children;
        return
    end
    
        9
  •  0
  •   Stef Heyenrath Dariusz Woźniak    14 年前

    下表:

    Id   ParentId
    1   NULL
    11    1
    12    1
    110 11
    111 11
    112 11
    120 12
    121 12
    122 12
    123 12
    124 12
    

    以及以下金额表:

    Id     Val
    110 500
    111 50
    112 5
    120 3000
    121 30000
    122 300000
    

    只有叶(最后一级)Id定义了值。 获取数据的SQL查询如下所示:

    ;WITH Data (Id, Val) AS
    (
        select t.Id, SUM(v.val) as Val from dbo.TestTable t
        join dbo.Amount v on t.Id = v.Id
        group by t.Id
    )
    
    select cd.Id, ISNULL(SUM(cd.Val), 0) as Amount FROM
    (
        -- level 3
        select t.Id, d.val from TestTable t
        left join Data d on d.id = t.Id
    
        UNION
    
        -- level 2
        select t.parentId as Id, sum(y.Val) from TestTable t
        left join Data y on y.id = t.Id
        where t.parentId is not null
        group by t.parentId
    
        UNION
    
        -- level 1
        select t.parentId as Id, sum(y.Val) from TestTable t
        join TestTable c on c.parentId = t.Id
        left join Data y on y.id = c.Id
        where t.parentId is not null
        group by t.parentId
    ) AS cd
    group by id
    

    Id     Amount
    1     333555
    11   555
    12   333000
    110 500
    111 50
    112 5
    120 3000
    121 30000
    122 300000
    123 0
    124 0
    

    我希望这能有帮助。