【问题标题】:Aggregative sum of objects belonging to objects residing inside hierarchy structure属于位于层次结构内部的对象的对象的总和
【发布时间】:2023-03-13 15:57:01
【问题描述】:

我的问题在某种程度上与this one 相似,但在我的理解上却大不相同。

我有三张桌子:

  1. Units ([UnitID] int, [UnitParentID] int)
  2. Students ([StudentID] int, [UnitID] int)
  3. Events ([EventID] int, [EventTypeID] int, [StudentID] int)

Students 属于单元,单元堆叠成层次结构(树形 - 一个父一个孩子),每个学生可以有不同类型的事件。

我需要总结每个用户的每种类型的事件数,然后聚合一个单元中的所有用户,然后通过层次结构聚合,直到我到达所有单元的母亲。

结果应该是这样的:

我的工具是 SQL Server 2008 和 Report Builder 3。 为了好玩,我贴了一个 SQL fiddle 和示例数据。

【问题讨论】:

  • 到目前为止你尝试过什么? SO 不是一种“为我编写代码”的服务......

标签: sql sql-server sql-server-2008 hierarchy


【解决方案1】:

使用这个查询:

;WITH CTE(Id, ParentId, cLevel, Title, ord) AS (
    SELECT 
        u.UnitID, u.UnitParentID, 1, 
        CAST('Unit ' + CAST(ROW_NUMBER() OVER (ORDER BY u.UnitID) AS varchar(3)) AS varchar(max)),
        CAST(RIGHT('000' + CAST(ROW_NUMBER() OVER (ORDER BY u.UnitID) AS varchar(3)), 3) AS varchar(max))
    FROM 
        dbo.Units u
    WHERE 
        u.UnitParentID IS NULL
    UNION ALL
    SELECT
        u.UnitID, u.UnitParentID, c.cLevel + 1, 
        c.Title + '.' + CAST(ROW_NUMBER() OVER (PARTITION BY c.cLevel ORDER BY c.Id) AS varchar(3)),
        c.ord + RIGHT('000' + CAST(ROW_NUMBER() OVER (ORDER BY u.UnitID) AS varchar(3)), 3)
    FROM
        dbo.Units u
        JOIN
        CTE c ON c.Id = u.UnitParentID
    WHERE 
        u.UnitParentID IS NOT NULL
), Units AS (
SELECT 
    u.Id, u.ParentId, u.cLevel, u.Title, u.ord,
    SUM(CASE WHEN e.EventTypeId = 1 THEN 1 ELSE 0 END) AS EventA,
    SUM(CASE WHEN e.EventTypeId = 2 THEN 1 ELSE 0 END) AS EventB,
    SUM(CASE WHEN e.EventTypeId = 3 THEN 1 ELSE 0 END) AS EventC,
    SUM(CASE WHEN e.EventTypeId = 4 THEN 1 ELSE 0 END) AS EventD
FROM 
    CTE u
    LEFT JOIN
    dbo.Students s ON u.Id = s.UnitId
    LEFT JOIN
    dbo.[Events] e ON s.StudentId = e.StudentId
GROUP BY
    u.Id, u.ParentId, u.cLevel, u.Title, u.ord
), addStudents AS (
SELECT *
FROM Units
UNION ALL
SELECT 
    s.StudentId, u.Id, u.cLevel + 1,
    'Student ' + CAST(s.StudentId AS varchar(3)),
    u.ord + RIGHT('000' + CAST(s.StudentId AS varchar(3)), 0),
    SUM(CASE WHEN e.EventTypeId = 1 THEN 1 ELSE 0 END),
    SUM(CASE WHEN e.EventTypeId = 2 THEN 1 ELSE 0 END),
    SUM(CASE WHEN e.EventTypeId = 3 THEN 1 ELSE 0 END),
    SUM(CASE WHEN e.EventTypeId = 4 THEN 1 ELSE 0 END)
FROM Units u
    JOIN
    dbo.Students s ON u.Id = s.UnitId
    LEFT JOIN
    dbo.[Events] e ON s.StudentId = e.StudentId
GROUP BY
    s.StudentID, u.ID, u.cLevel, u.ord
)
SELECT --TOP(10)
    REPLICATE(' ', cLevel) + Title As Title, 
    EventA, EventB, EventC, EventD
FROM
    addStudents 
ORDER BY
    ord

为此:

Title            | EventA | EventB  | EventC | EventD
-----------------+--------+---------+--------+--------
 Unit 1          | 0      | 1       | 0      | 0
  Student 6      | 0      | 1       | 0      | 0
  Unit 1.1       | 0      | 0       | 0      | 1
   Student 21    | 0      | 0       | 0      | 1
   Student 33    | 0      | 0       | 0      | 0
   Unit 1.1.1    | 0      | 0       | 0      | 0
    Student 23   | 0      | 0       | 0      | 0
    Unit 1.1.1.1 | 3      | 2       | 3      | 0
     Student 10  | 0      | 0       | 0      | 0
     Student 17  | 1      | 0       | 0      | 0
...

SQL Fiddle Demo

【讨论】:

  • 这仅将事件汇总到上一级。我认为我们的想法是将它们一直计算到顶部。
  • @JamesZ 我认为第一个 CTE 会做到这一点;)。
  • @shA.t 谢谢!但我也看不出这是如何一直聚合到顶层的。此外,我确实需要保留单位和学生的原始名称。
  • 首先CTE准备units的所有级别,计算每个节点的当前级别为cLevel,节点的当前序列作为您在title中的需要,并为订购后准备一个字段为@ 987654328@ - 然后在第二个 CTE 中计算所有 units 的总和 - 在最后一个 CTE 中,我将学生添加到结果中 - 然后显示您的预期结果,但是如果您想要其他字段,您可以将它们添加到您的查询中;)
【解决方案2】:

您还需要对层次结构进行排序/可视化吗?至少这会计算总和,但数据的顺序是非常随机的:)

;with CTE as (
  select S.StudentId as UnitID, S.UnitId as UnitParentID,
    S.StudentID, 'Student' as Type
  from Students S
union all
  select U.UnitId, U.UnitParentId, 
    CTE.StudentId as StudentID, 'Unit   ' as Type
  from
    Units U
    join CTE
      on U.UnitId = CTE.UnitParentId
)
select C.Type + ' ' + convert(varchar, C.UnitId), 
  sum(case when EventTypeId = 1 then 1 else 0 end) as E1,
  sum(case when EventTypeId = 2 then 1 else 0 end) as E2,
  sum(case when EventTypeId = 3 then 1 else 0 end) as E3,
  sum(case when EventTypeId = 4 then 1 else 0 end) as E4
from 
  CTE C
  left outer join events E on C.StudentId = E.StudentId
group by
  C.Type, C.UnitId

SQL Fiddle

如果您还需要按顺序排列层次结构,您可能需要添加一些额外的 CTE 以使用 @shA.t 之类的方法从上到下进行编号。这会为每个学生单独收集层次结构,因此实际上不可能以简单的方式添加级别编号。

【讨论】:

  • 谢谢!我确实需要可视化层次结构。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多