将一组行显示为单行

时间:2018-06-22 09:20:36

标签: sql sql-server tsql gaps-and-islands

我有一个包含以下行的表:

MsgID |       DateTime       | State | TimeDiff
---------------------------------------------
387   | 2014-09-06 21:06:27  | 2     | 43
387   | 2014-09-06 21:06:28  | 3     | 44
212   | 2014-09-06 22:16:27  | 1     | 0
212   | 2014-09-06 22:16:38  | 3     | 11
532   | 2014-09-06 23:26:27  | 1     | 0
532   | 2014-09-06 23:27:27  | 3     | 60
532   | 2014-09-06 23:27:28  | 2     | 61
215   | 2014-09-06 23:46:27  | 1     | 0
212   | 2014-09-07 22:16:27  | 1     | 0
212   | 2014-09-07 22:16:37  | 2     | 10
212   | 2014-09-07 22:16:38  | 3     | 11

我想像这样合并这些行:

MsgID |       State 1       |       State 2       |       State 3       | 
-------------------------------------------------------------------------
212   | 2014-09-06 22:16:27 | null                | 2014-09-06 22:16:38 |
532   | 2014-09-06 23:26:27 | 2014-09-06 23:27:28 | 2014-09-06 23:27:27 |
215   | 2014-09-06 23:46:27 | null                | null                |
212   | 2014-09-07 22:16:27 | 2014-09-07 22:16:37 | 2014-09-07 22:16:38 |

在此示例中,MsgID = 387的行被忽略,因为它们没有开始条件(状态1)。

我不知道该如何解决。

3 个答案:

答案 0 :(得分:2)

您可以在如下查询中使用PIVOT语句。

请注意,您必须熟悉 PIVOT 语法。 最里面的查询用于限定消息ID,以便不占用MsgId(例如387)。 上面带有row_number()的查询是为了帮助识别差距。这是类型问题的常见解决方案。查看有关缺口和孤岛的有用链接 here

See working demo

select 
 MsgID,
 State1= [1],
 State2= [2],
 State3=[3]
from 
(
    select 
    t1.MsgID,
    t1.DateTime,
    T1.State,
    r=row_number() over ( order by t1.[DateTime])- row_number() over( partition by t1.MsgID order by t1.[Datetime])
from t t1
join 
(
    select MsgId 
    from t 
    group by MsgID 
    having min(State)=1
) t2
on t1.MsgId=t2.MsgId
) src
PIVOT
(
    max(DateTime) for State in ([1],[2],[3])
    )p

答案 1 :(得分:1)

您可以使用差异row_numbers并进行有条件的聚合:

select msgid, max(case when state = 1 then datetime end) state1,
              max(case when state = 2 then datetime end) state2,
              max(case when state = 3 then datetime end) state3
from (select *, row_number() over (partition by msgid order by [datetime]) seq1,
                row_number() over (partition by msgid, cast(datetime as date) order by datetime) seq2
      from table t
      where exists (select 1 from table t1 where t1.msgid = t.msgid and t1.state = 1)
     ) t
group by msgid, (seq1-seq2);

答案 2 :(得分:0)

我不认为这是一个孤岛问题。按消息ID和日期进行简单汇总可用于您的示例数据:

select msgid,
       max(case when state = 1 then datetime end) as state1,
       max(case when state = 2 then datetime end) as state2,
       max(case when state = 3 then datetime end) as state3
from t
group by msgid, cast(datetime as date)
having min(t.state) = 1;

注意:这假定您可能具有的最小状态为“ 1”。否则,您可以使用:

having sum(case when t.state = 1 then 1 else 0 end) > 0;