【问题标题】:T-SQL - Querying Hierarchical Table with Running Subtotal (each row has an item count)T-SQL - 使用运行小计查询分层表(每行都有一个项目计数)
【发布时间】:2011-05-28 23:05:09
【问题描述】:

我正在尝试基于遵循此设计的分层表创建动态导航:

CREATE TABLE #CPTree (UID int, ParentID int, Name nvarchar(150), ItemCount int)

插入 #CPTree 值('1'、'0'、'Vehicles'、'0')

插入 #CPTree 值('2'、'1'、'Bikes'、'10')

插入 #CPTree 值('3'、'1'、'Cars'、'20')

插入 #CPTree 值('5'、'2'、'Bike Make 1'、'0')

插入 #CPTree 值('6'、'2'、'Bike Make 2'、'5')

INSERT INTO #CPTree 值(“7”、“3”、“汽车制造商 1”、“0”)

插入 #CPTree 值(“8”、“3”、“汽车制造商 2”、“0”)

插入 #CPTree 值('9'、'5'、'Bike Model A'、'7')

插入 #CPTree 值('10'、'5'、'Bike Model B'、'1')

INSERT INTO #CPTree 值('11'、'7'、'Car Model D'、'4')

INSERT INTO #CPTree 值('12'、'8'、'Car Model X'、'2')

--使用 CTE 检索类别/子类别的级别

;with HCTE(CategoryID, ParentID, Name, itemcount, Level) as (select UID, ParentID, Name, itemcount, 0 as Level from #CPTree c where c.UID = 3 -- 汽车类别

union all select c.UID, c.ParentID, c.Name, c.itemcount, ch.Level + 1 from #CPTree c inner join HCTE ch on ch.CategoryID = c.ParentID )

从 HCTE 中选择 *

删除表#CPTree

--结束

我想检索每个类别的总项目数,例如顶级类别“汽车”(项目数 20)有 2 个孩子(两个项目数均为 0),并且每个都有孩子(2 和恭敬地计算 4 项)。 Cars 的总项目数为 26。同样,Car Make 1(CategoryID 7)的总项目数为 4(其子项的总和 - CategoryID 11)。结果将返回:

类别 ID |家长 ID |姓名 |项目计数 |水平 | 项目总数

3 | 1 |汽车 | 20 | 0 | 26

7 | 3 |汽车制造 1 | 0 | 1 | 4

8 | 3 |汽车制造2 | 0 | 1 | 2

12 | 8 | X 型车 | 2 | 2 | 2

11 | 7 |车型 D 4 | 2 | 4

这将使我能够查看子类别是否在其子类别中包含任何项目,但不一定它们本身是否有任何项目。我的活动表有很多级别,因此查询必须继续钻取类别,直到达到最后一个级别。如有必要,我可以提供更深入的示例。

感谢任何帮助!谢谢

【问题讨论】:

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


    【解决方案1】:

    使用的想法是为树中的每个节点添加一个祖先的踪迹(作为一个字符串),然后总结所有包含该节点的行。

       ;with HCTE(CategoryID, ParentID, Name, itemcount, Level, trail)
        as
        (
         select UID, ParentID, Name, itemcount, 0 as Level, 
                                                      '/' + cast(UID as varchar(max))+ '/' as trail 
         from #CPTree c
         where c.UID = 3 -- Cars Category
    
    
    union all
    
    select c.UID, c.ParentID, c.Name, c.itemcount, ch.Level + 1, 
                                                   trail + CAST(UID as varchar(max)) + '/'
    from #CPTree c 
    inner join HCTE ch on ch.CategoryID = c.ParentID
    )
    select CategoryID, ParentID, Name, ItemCount,
       (select SUM(ItemCount)
        from HCTE h2
        where charindex('/' + CAST(h1.CategoryID as varchar(max)) + '/', h2.trail, 1) <> 0
       )
     from hcte h1
    

    【讨论】:

      【解决方案2】:

      在您的简单示例中,将您的 select 语句替换为下面的 select。

      SELECT , (select COUNT() from HCTE b where a.Level+1 = b.Level) Children FROM HCTE a

      它非常简单,但是如果你有很多很多关卡,那么它就不会很好地扩展。

      但您可以在 select 语句中多次引用 CTE。

      【讨论】:

      • 感谢 MarkPm,这将为我提供每个类别的子项计数,但我需要将每个类别的 itemcount 与它的父项相加。我可以这样做:SELECT *, (select sum(itemcount) from HCTE b where a.Level+1 = b.Level and a.CategoryId = B.ParentId) as TotalItemCount FROM HCTE a,但它不会求和到顶级类别
      【解决方案3】:

      我知道你已经接受了一个答案,但我会认真看看来自 DWH 世界的this technique

      您接受的查询有 5 个 from 子句,根据优化器如何/是否实现 HCTE,您将多次读取和加入同一个表。

      该技术本质上构建了一个“分层索引”。像所有其他索引一样,您将花时间插入/更新/删除以节省查询时间。和其他所有索引一样,您也需要花费一点空间。

      最终,您有一种非常快速的方法来获得您想要的确切答案。

      根据您的需要,如果对汽车品牌和型号表的更新频繁且不定期,则可以通过触发器维护“索引”。或者,如果您每天加载该表一次,您可以用一条语句重建“索引”。

      【讨论】:

      • 谢谢斯蒂芬妮,我去看看
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-03-19
      • 1970-01-01
      • 1970-01-01
      • 2016-05-08
      • 2016-09-14
      • 1970-01-01
      • 2023-03-10
      相关资源
      最近更新 更多