在MS SQL 2012的时间跨度内每半小时间隔聚合一次

时间:2017-01-13 21:57:19

标签: sql sql-server sql-server-2012

考虑下表:

CREATE TABLE dbo.Events (
    [ClientID] int,
    [EventID] int,
    [EventName] varchar(50),
    [StartDate] datetime,
    [Duration] bigint
)

INSERT INTO dbo.Events(ClientID, EventID, EventName, StartDate, Duration) 
VALUES (1, 1, 'Login', '2016-11-27 01:30:00.000', 86400000),
       (2, 1, 'Login', '2016-11-27 00:30:00.000', 86400000),
       (3, 1, 'Login', '2016-11-27 00:00:00.000', 86400000),
       (4, 1, 'Login', '2016-11-28 23:30:00.000', 172800000),
       (1, 2, 'Lock', '2016-11-27 23:30:00.000', 3600000),
       (4, 2, 'Lock', '2016-11-28 23:30:00.000', 1800000)

如您所见,持续时间以毫秒为单位,可以跨越多天。

我需要查看已登录的客户端数量,已锁定的客户端数量以及给定日期范围内的持续时间。日期范围可以跨越一天/一周/一个月/一年,当它超过一天时,它应该仍然需要半小时。

最后,日期范围2016-11-28 00:00:00.000 - 2016-11-30 00:00:00.000的查询结果看起来如此(如果我错误计算SumDuration或CountClients,请为此道歉在某个地方,我手动完成了这个,但我认为这是正确的):

EventID  EventName  HalfHour  SumDuration  CountClients
--------------------------------------------------
1        'Login'    0:00      5400000      3 
1        'Login'    0:30      3600000      2 
1        'Login'    1:00      3600000      2
1        'Login'    1:30      1800000      1 
...      ...        ...       ...          ...
1        'Login'    23:00     1800000      1 
1        'Login'    23:30     3600000      1
2        'Lock'     0:00      3600000      2 
2        'Lock'     0:30      1800000      1 
...      ...        ...       ...          ...
2        'Lock'     23:00     1800000      1 
2        'Lock'     23:30     3600000      2 

省略号是为了重复结果的简洁,但每个事件应该有48个结果(一天中每半小时1个)。您还会注意到需要在半小时内计算持续时间。它不在示例数据中,但可以随时结束持续时间,而不仅仅是半小时标记。最后,我应该在23:30注意“登录”的结果。 ClientID 4在2016-11-28和2016-11-29期间在23:30-0:00半小时内举行了登录活动。因此,持续时间总和两者,但只计算客户一次。

我有以下内容,它为我提供了CountClients和SumDuration,但不会将其分成半小时的增量:

select E.EventID,
       E.EventName,
       count(*) as CountClients,
       sum(datediff(millisecond, I.StartDate, I.EndDate)) as SumDuration
from dbo.Events as E
  cross apply (
              select max(T.StartDate) as StartDate,
                     min(T.EndDate) as EndDate
              from (
                   values(@StartDate, @EndDate),
                         (E.StartDate, dateadd(millisecond, E.Duration, E.StartDate))
                   ) as T(StartDate, EndDate)
              ) as I
where E.StartDate < @EndDate and
      dateadd(millisecond, E.Duration, E.StartDate) > @StartDate
group by E.EventID,
         E.EventName;

我如何将其分解为半小时的增量?

1 个答案:

答案 0 :(得分:1)

DECLARE @FirstDay smalldatetime = '20161128', 
        @LastDay  smalldatetime = '20161129';

;WITH TimeSlots(HalfHour) AS 
(
  SELECT TOP (24*2*DATEDIFF(DAY, @FirstDay, DATEADD(DAY,1,@LastDay)))
    DATEADD(MINUTE, 30*(ROW_NUMBER() OVER (ORDER BY (SELECT NULL))-1), @FirstDay)
  FROM master..spt_values
),
EventRange AS
(
  SELECT EventID, EventName, StartDate, 
    EndDate = DATEADD(MILLISECOND, Duration, StartDate)
  FROM dbo.Events
  WHERE StartDate >= DATEADD(MILLISECOND, -Duration, @FirstDay)
    AND StartDate <  DATEADD(DAY, 1, @LastDay)
),
Combo AS
(
  SELECT e.EventID, e.EventName, 
    HalfHourSlot = CONVERT(CHAR(5), CONVERT(datetime, CONVERT(time, t.HalfHour)), 108), 
    CASE
        WHEN 
            e.StartDate <= t.HalfHour AND e.EndDate > t.HalfHour AND e.EndDate < DATEADD(minute, 30, t.HalfHour)
        THEN DATEDIFF(MILLISECOND, e.EndDate, t.HalfHour)
        WHEN
            e.StartDate <= t.HalfHour AND e.EndDate > t.HalfHour AND e.EndDate >= DATEADD(minute, 30, t.HalfHour)
        THEN 1800000
        ELSE
            0
        END AS Duration
  FROM TimeSlots AS t
  CROSS JOIN EventRange AS e
)
SELECT EventID, EventName, HalfHour = HalfHourSlot,
  SumDuration  = SUM(Duration), 
  CountClients = SUM(CASE WHEN Duration>0 THEN 1 ELSE 0 END) 
FROM Combo
GROUP BY EventID, EventName, HalfHourSlot
ORDER BY EventName DESC, HalfHour;