如何使用重叠的开始和停止时间 (SQL)(t-SQL) 对 15 分钟间隔内发生的 activity 时间求和

How to sum the activity time that occurred within 15-minute intervals using overlapping start and stop times(SQL)(t-SQL)

我想编写一个查询,仅使用对应于 activity 开始和停止时间的时间戳来计算一天中每 15 分钟间隔内发生的 activity 总量.

这是一个示例数据集:

    DATE    StartDateTime   StopDateTime
2/2/2015    2/2/2015 7:00   2/2/2015 7:25
2/2/2015    2/2/2015 7:20   2/2/2015 7:29
2/2/2015    2/2/2015 7:35   2/2/2015 7:42
2/2/2015    2/2/2015 8:05   2/2/2015 8:14
2/2/2015    2/2/2015 8:16   2/2/2015 8:20
2/2/2015    2/2/2015 8:29   2/2/2015 8:40
2/2/2015    2/2/2015 8:55   2/2/2015 9:25

这就是我希望能够得到的:

    DATE    Interval       activityTime(min)
2/2/2015    2/2/2015 7:00   15
2/2/2015    2/2/2015 7:15   19
2/2/2015    2/2/2015 7:30   7
2/2/2015    2/2/2015 7:45   0
2/2/2015    2/2/2015 8:00   9
2/2/2015    2/2/2015 8:15   5
2/2/2015    2/2/2015 8:30   10
2/2/2015    2/2/2015 8:45   5
2/2/2015    2/2/2015 9:00   15
2/2/2015    2/2/2015 9:15   10

我一直在寻找一种方法来按照我需要的方式组织数据,这是迄今为止我能找到的最接近的方法,但我还没有能够让它发挥作用:

Splitting time + duration into intervals in t-sql

我是 SQL 的新手,因此非常感谢对解决方案的任何解释。这也是我在 Whosebug 上的第一个 post,所以如果数据不是首选格式或有任何其他问题,请告诉我。谢谢!

假设 SQL 服务器有一些合理的最新版本,这应该是一个好的开始:

-- Some sample data.
declare @Samples as Table ( SampleId Int Identity, Start DateTime, Stop DateTime );
insert into @Samples ( Start, Stop ) values
  ( '2/2/2015 7:00', '2/2/2015 7:25' ),
  ( '2/2/2015 7:20', '2/2/2015 7:29' ),
  ( '2/2/2015 7:35', '2/2/2015 7:42' ),
  ( '2/2/2015 8:05', '2/2/2015 8:14' ),
  ( '2/2/2015 8:16', '2/2/2015 8:20' ),
  ( '2/2/2015 8:29', '2/2/2015 8:40' ),
  ( '2/2/2015 8:55', '2/2/2015 9:25' );
select * from @Samples;

-- Find the limits and align them to quarter hours.
declare @Min as DateTime;
declare @Max as DateTime;
select @Min = min( Start ), @Max = max( Stop )
  from @Samples;
set @Min = DateAdd( minute, -DatePart( minute, @Min ) % 15, @Min );
set @Max = DateAdd( minute, 15 - DatePart( minute, @Max ) % 15, @Max );
select @Min as [Min], @Max as [Max];


-- Go for it.
with QuarterHours ( QuarterStart, QuarterStop )
  as (
    select @Min, DateAdd( minute, 15, @Min )
    union all
    select QuarterStop, DateAdd( minute, 15, QuarterStop )
      from QuarterHours
      where QuarterStop < @Max ),
  Overlaps
  as ( select QH.QuarterStart, QH.QuarterStop, S.Start, S.Stop,
  case
    when S.Start <= QH.QuarterStart and S.Stop >= QH.QuarterStop then 15
    when S.Start <= QH.QuarterStart and S.Stop < QH.QuarterStop then DateDiff( minute, QH.QuarterStart, S.Stop )
    when S.Start > QH.QuarterStart and S.Stop >= QH.QuarterStop then DateDiff( minute, S.Start, QH.QuarterStop )
    when S.Start > QH.QuarterStart and S.Stop < QH.QuarterStop then DateDiff( minute, S.Start, S.Stop )
    else 0 end as Overlap
  from QuarterHours as QH left outer join
    @Samples as S on S.Start <= QH.QuarterStop and S.Stop >= QH.QuarterStart )
  select QuarterStart, sum( Overlap ) as [ActivityTime]
    from Overlaps
    group by QuarterStart
    order by QuarterStart;

您可以将最后一个 select 更改为 select * from QuarterHoursselect * from Overlaps 以查看一些中间值。

注释:

您可以使用您想要的任何范围 (@Min/@Max),我只是从示例数据中提取它们,因此该示例将 运行。出于同样的原因,我使用了 table 变量,为了示例,无需创建 "real" table。

通用 Table 表达式 (CTE) 通过递归创建覆盖所需范围的 QuarterHours 的 table。 (numbers tabletally table 也可用于生成刻钟。)然后 LEFT OUTER JOIN 与示例数据用于定位所有 Overlaps(如果有),每刻钟。这保留了没有 activity.

的刻钟

最后SELECT总结结果

以下查询将为您提供每个 15 分钟的增量,其中至少包含一个开始时间和从该 15 分钟增量开始的整个持续时间的 activity 总量(以分钟为单位)。

select  Date,
        Convert( SmallDatetime, Floor( Cast( StartDateTime as float ) * 96.0 ) / 96.0 ) Increment,
        Sum( DateDiff( second, StartDateTime, StopDateTime )) / 60 Duration
from    Activities
group by Date, Convert( SmallDatetime, Floor( Cast( StartDateTime as float ) * 96.0 ) / 96.0 );

哪个returns这个:

Date       Increment           Duration
---------- ------------------- --------
2015-02-02 2015-02-02 07:00:00 25
2015-02-02 2015-02-02 07:15:00  9
2015-02-02 2015-02-02 07:30:00  7
2015-02-02 2015-02-02 08:00:00  9
2015-02-02 2015-02-02 08:15:00 15
2015-02-02 2015-02-02 08:45:00 30

我正在考虑计算 运行 总计溢出到下一个增量,这时我想到了一些事情。一是您将需要在查询期间每 15 分钟增加一次,无论是否有任何 activity 开始。因此,我们必须使用计数 table 来确保生成每个间隔,如果没有别的,以捕获前一个间隔的一些溢出分钟数。

然后,当然要跟踪 运行 溢出的总数。虽然这是可能的(请参阅 以了解有关操作方法的详细解释),但令我震惊的是,两者的结合(合计 table 和 运行 总计)是一个非常大的开销在 SQL 中执行。请记住,在 SQL 中执行计算甚至比最快的磁盘访问快很多倍,但是在任何高级语言(Java、C++、C# 甚至 Perl 等脚本语言)中执行计算都将是比 SQL 快很多倍。此外,SQL 解决方案的可维护性将深入地下室。

所以我现在的建议是采用上面的查询并将其提供给一个好的报告引擎或您的应用程序,然后让它们执行额外的计算。在性能方面,您将遥遥领先。