7

我需要总结用户树获得的每个级别的积分。级别 1 是用户下一级用户的用户积分之和。2级是用户低于用户2级的用户的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 语法和函数最好...

4

9 回答 9

2

如果您使用的是 Oracle DBMS,那将非常简单,因为 Oracle 支持使用CONNECT BY/STARTS WITH语法的树查询。对于 SQL Server,我认为您可能会发现Common Table Expressions很有用

于 2008-09-18T10:10:55.933 回答
2

树不适用于 SQL。如果您有非常(非常非常)很少的写访问,您可以更改树实现以使用嵌套集,这将使此查询非常容易。

示例(如果我没记错的话):

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

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

于 2008-09-18T10:11:51.797 回答
1

Ok, this gives you the results you are looking for, but there are no guarantees that I didn't miss something. Consider it a starting point. I used SQL 2005 to do this, SQL 2000 does not support CTE's

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
于 2008-09-18T14:44:54.760 回答
1

我会说:创建一个存储过程,可能有最好的性能。或者,如果您有最大数量的级别,您可以创建子查询,但它们的性能会很差。

(或者您可以获得 MS SQL Server 2008 并获得新的层次结构函数......;))

于 2008-09-18T10:06:56.113 回答
1

如果您正在使用存储在关系数据库中的树,我建议您查看“嵌套集”或“修改的预序树遍历”。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或使用谷歌。

于 2008-09-18T10:14:49.090 回答
1

就像其他人所说的那样,一般的 SQL 不能很好地处理这种关系。通常,需要一个代理“关系”表(id,parent_id,(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];

  • 有逻辑避免循环

  • 确保使用存储过程处理“关系”上的更新、删除

鉴于该表,您想要:

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;
于 2008-09-18T12:05:23.880 回答
0

你有几个选择:

  1. 使用游标和递归用户定义函数调用(它很慢)
  2. 创建一个缓存表,使用触发器在 INSERT 上更新它(这是最快的解决方案,但如果您对主表进行大量更新,则可能会出现问题)
  3. 做一个客户端递归计算(如果你没有太多的记录最好)
于 2008-09-18T10:12:51.840 回答
0

您可以编写一个简单的递归函数来完成这项工作。我的 MSSQL 有点生锈,但看起来像这样:

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
于 2008-09-18T10:23:21.550 回答
0

下表:

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

我希望这有帮助。

于 2010-12-13T13:13:57.303 回答