根据列值在sql中创建子组聚合

时间:2015-04-21 19:33:11

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

我正在尝试从数据中的现有记录创建子记录聚合。 以此示例数据为例:

Index   Date    Action
1   1/1/2015    Working
2   1/2/2015    Working
3   1/3/2015    Working
4   1/4/2015    Escalated
5   1/5/2015    Done
6   1/6/2015    Working
7   1/7/2015    Done
8   1/8/2015    Working
9   1/9/2015    Working
10  1/10/2015   Working
11  1/11/2015   Escalated
12  1/12/2015   Done
13  1/13/2015   Done
14  1/14/2015   Working  

我希望能够创建这些数据:

Record  DateBegin   DateEnd #Actions    #Escalations
A   1/1/2015        1/5/2015    5       1
B   1/6/2015        1/7/2015    2       0
C   1/8/2015        1/12/2015   5       1
D   1/13/2015       1/13/2015   1       0
E   1/14/2015       null        1       0

基本上,逻辑是当Action值='Done'时子记录结束,并且新的子记录在任何后续动作(以及第一个动作)上开始。 我正在使用SQL Server 2008.感谢您的帮助!

3 个答案:

答案 0 :(得分:0)

这是一张设计糟糕的桌子;应该有一个TaskID来识别特定任务(一旦有人在前一个任务完成之前启动任务,这个结构将变得无法使用)。

我会:

  1. 使用Index,Date,Action和TaskID创建临时表

  2. 写一个游标来迭代旧表,按索引排序。有一个局部变量CurrentTaskID(初始化为1)。对于每个读取记录,将Index,Date,Action和CurrentTaskID写入新表。在每次写入之后,查看Action - 如果它='DONE'然后递增CurrentTaskID

  3. 在临时表上写一个类似的查询:

    SELECT min(a.date),max(a.date),count(*)
    ,(SELECT count(*) FROM MyTempTable b WHERE b.TaskID = a.TaskID AND b.Action ='Escalated')
    FROM MyTempTable a GROUP BY a.TaskID

答案 1 :(得分:0)

有趣的问题。试试这个,应该有用。

;with dones as (
    select
        nn = ROW_NUMBER() over(order by [Date])
        ,*
    from YourTable
    where action = 'Done'

), ranges as (
    select
        [Record] = CHAR(ASCII('A') - 1 + ISNULL(d2.nn, d1.nn + 1))  --  A,B,C,... - after 255 will give NULL
        ,dtFrom = d1.[Date]
        ,dtTo = d2.[Date]
    from dones d1
    full join dones d2 on d1.nn = d2.nn + 1

)
    select 
        dones.[Record]
        ,DateBegin  = MIN(tt.[date]) 
        ,DateEnd = dones.dtTo
        ,[#Actions] = COUNT(tt.*)
        ,[#Escalations] = SUM(case when tt.Action = 'Escalated' then 1 else 0 end)
    from YourTable tt
    inner join dones 
        on (dones.dtFrom is null or tt.[date] > dones.dtFrom ) 
        and (dones.dtTo is null or tt.[date] <= dones.dtFrom)
    group by dones.[Record], dones.dtTo;

答案 2 :(得分:0)

您可以通过计算每条记录前done条记录的数量来分配分组参数。其余的只是聚合,虽然为每个组分配一个字母似乎是不必要的复杂化:

select grp as record, min(Date) as DateBegin,
       max(case when Action = 'Done' then Date end) as DateEnd,
       count(*) as NumActions,
       sum(case when Action = 'Escalation' then 1 else 0 end) as NumEscalations
from (select e.*, coalesce(e2.grp, 0) as grp
      from example e outer apply
           (select count(*) as grp
            from example e2
            where e2.id < e.id and e2.Action = 'Done'
           ) e2
     ) e
group by grp;

此查询在SQL Server 2012+中更简单(也更高效),它支持累积总和。

编辑:

我注意到我使用子查询,但这不是必需的。这可以写成:

      select coalesce(grp, 0) as record, min(Date) as DateBegin,
             max(case when Action = 'Done' then Date end) as DateEnd,
             count(*) as NumActions,
             sum(case when Action = 'Escalation' then 1 else 0 end) as NumEscalations
      from example e outer apply
           (select count(*) as grp
            from example e2
            where e2.id < e.id and e2.Action = 'Done'
           ) e2
      group by e2.grp