计算来自相同列值的中间行

时间:2017-02-03 07:30:55

标签: sql sql-server

在Microsoft SQL Server中,如何根据同一列中的值计算中间行。

实施例

/-----------------------------------------\
| ID ---------- Event -------- UserID ----|
| 1 ----------- START -------- 000001 ----|
| 2 ----------- START -------- 000002 ----|
| 3 ----------- END   -------- 000001 ----|
| 4 ----------- PL    -------- 000002 ----|
| 5 ----------- END   -------- 000002 ----|
\-----------------------------------------/

考虑UserID 000002,其中有3行,ID为2,4& 5。 根据此link的查询,我可以获得STARTEND时间,但如何在{{1}之间获得行数每个START

END

预期结果

USERID

2 个答案:

答案 0 :(得分:4)

SELECT USERID , sum(CASE WHEN MainT.ID BETWEEN StartT.ID AND EndT.ID THEN 1 ELSE 0 END) AS RowCount FROM Table1 AS MainT INNER JOIN Table1 AS StartT ON MainT.USERID = StartT.USERID AND StartT.event = 'START' INNER JOIN Table1 AS EndT ON MainT.USERID = EndT.USERID AND EndT.event = 'END' GROUP BY USERID

这应该产生所需的输出,具有以下假设:

  • ID是(自动)递增,因此用户的START事件具有最低ID。希望真实数据中有时间戳。
  • 每个相关用户都有一个START和零个或一个END事件,但不包括在数据集之外开始或结束的用户。

回应评论:

通常情况下,您无法显示""未被分组的字段的值,因为可能有多个值在播放。这意味着您需要告诉SQL服务器如何使用某种聚合函数处理值。

如果您确定每组只有一个可能的值,您可以作弊并采取min(事件)或max(事件)。这会为您提供组中显示的第一个或最后一个按字母顺序的值。这是有风险的,如果您稍后在该表中包含更多事件,则可能会导致问题。

如果只有几个,您可以按事件类型拆分计数:

sum(CASE WHEN **MainT.event ='EventA' AND** MainT.ID BETWEEN StartT.ID AND EndT.ID THEN 1 ELSE 0 END) AS EventACount, sum(CASE WHEN **MainT.event ='EventB' AND** MainT.ID BETWEEN StartT.ID AND EndT.ID THEN 1 ELSE 0 END) AS EventBCount,

最后,您可以使用某种列表聚合,连接组中出现的所有事件类型。 This question详细介绍了这一点。

答案 1 :(得分:1)

该方案没有明确定义,您可以在不同的解决方案中看到它。

这将处理简单的用例。

select      UserID
           ,count(*)    as cnt
from        mytable

这将处理复杂的用例。

select      UserID
           ,min(ID)     as from_ID
           ,max(ID)     as to_ID
           ,count(*)    as events

from       (select      UserID,ID,Event
                       ,    count(case when Event in ('START','END') then 1 end) over 
                            (
                                partition by    UserID 
                                order by        Id 
                                rows            unbounded preceding
                            )   
                        -   case when Event = 'END' then 1 else 0 end   as group_seq

            from        mytable
            ) t

group by    UserID
           ,group_seq

having      min(case when Event = 'START' then 1 end) = 1

order by    UserID
           ,from_id