【问题标题】:What is the best way to group and aggregate and sum tree data?对树数据进行分组、聚合和求和的最佳方法是什么?
【发布时间】:2010-12-09 07:32:42
【问题描述】:

给定一个自引用表

Item 
-------------
Id (pk)
ParentId (fk)

带有关联值的相关表

ItemValue
-------------
ItemId (fk)
Amount

还有一些样本数据

Item                       ItemValues 
Id      ParentId           ItemId      Amount
--------------------       ----------------------
1       null               1           10
2       1                  3           40
3       1                  3           20
4       2                  4           10
5       2                  5           30
6       null
7       6
8       7

我需要一个 sproc 来获取 Item.Id 并返回直接子代以及所有 ItemValue.Amounts 的总和,以供他们、他们的孩子和他们的孩子一路向下。

例如,如果传入1,则树将为2, 3, 4, 5,直接子节点为2, 3,则输出为

 ItemId    Amount
 ------------------
 2         40     (values from ItemIds 4 & 5)
 3         60     (values from ItemId 3)

应该采用什么样的方法来实现这种行为?

我正在考虑使用 CTE,但想知道是否有更好/更快的方法。

【问题讨论】:

  • SQL Server 2005,我也会重新标记

标签: sql sql-server sql-server-2005 tsql hierarchical


【解决方案1】:

假设您的层次结构不会太深,这样的递归 CTE 会起作用:

declare @ParentId int;
set @ParentId = 1;

;with 
  Recurse as (
    select 
      a.Id as DirectChildId
    , a.Id
    from Item a 
    where ParentId = @ParentId
    union all
    select
      b.DirectChildId
    , a.Id
    from Item a 
    join Recurse b on b.Id = a.ParentId
    )
select
  a.DirectChildId, sum(b.Amount) as Amount
from Recurse a
left join ItemValues b on a.Id = b.ItemId
group by
  DirectChildId;

非 CTE 方法需要某种形式的迭代,基于游标或其他。因为它是一个存储过程,所以它是一种可能性,如果有很多数据需要递归,它可能会更好地扩展,只要你对数据进行适当的切片。

如果聚集索引在 Id 上,则在 ParentId 上添加非聚集索引。作为一个覆盖索引,它将满足初始搜索而无需书签查找。然后聚集索引将帮助递归连接。

如果聚集索引已经在 ParentId 上,则在 Id 上添加一个非聚集索引。总之,它们几乎等同于上述内容。对于 ItemValues,如果实际表比这宽,您可能需要 (ItemId) INCLUDE (Amount) 上的索引。

【讨论】:

  • “假设你的层次结构没有太深”它没有,但如果它这样做会有什么问题?
  • 在 SQL Server 上,递归深度默认限制为 100。您可以使用 MAXRECURSION 查询提示指定高达 32767,但我假设它不会随着超过某个阈值的音量线性缩放。不过,对于您的情况,这似乎不是问题,因为您将一次指定一个父母。只要你有正确的索引并且递归深度是合理的(低于 100?),性能应该不是问题。
  • 您可以使用 CTE 进行无限递归。设置 MAXRECURSION = 0。它在 BOL 中表示。
【解决方案2】:

您能否将数据存储为嵌套集模型(这里是 MySQL reference,但这些想法是跨数据库通用的)?如果是这样,那么找到您正在寻找的值的操作将相当简单。

【讨论】:

    【解决方案3】:

    这必须在数据库中处理吗?我建议将必要的数据带入您的 BLL 并在那里执行递归。

    【讨论】:

      猜你喜欢
      • 2017-12-27
      • 1970-01-01
      • 1970-01-01
      • 2010-09-06
      • 2020-02-17
      • 2018-10-06
      • 2018-02-16
      • 2012-03-05
      • 1970-01-01
      相关资源
      最近更新 更多