【问题标题】:How to sum the activity time that occurred within 15-minute intervals using overlapping start and stop times(SQL)(t-SQL)如何使用重叠的开始和停止时间(SQL)(t-SQL)对 15 分钟间隔内发生的活动时间求和
【发布时间】:2015-06-13 21:55:39
【问题描述】:

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

这是一个示例数据集:

    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 还很陌生,因此对解决方案的任何解释都将不胜感激。这也是我在 stackoverflow 上的第一篇文章,所以如果数据不是首选格式或有任何其他问题,请告诉我。谢谢!

【问题讨论】:

  • 使用适当的软件(MySQL、Oracle、DB2...)和版本标记数据库问题很有帮助,例如sql-server-2014。语法和功能的差异通常会影响答案。
  • 谢谢。我已对其进行了更新,以包括 teradata sql 助手和 sql 版本。
  • Teradata 和 SQL Server 都需要这个吗?您的 Teradata 版本是什么?
  • 我的意思是放第 14 版 teradata,而不是 sql server。我们可能很快就会过渡到 SQL Server,但现在,我正在使用 teradata

标签: tsql datetime teradata timetable


【解决方案1】:

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

-- 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),我只是从示例数据中获取它们以便示例运行。出于同样的原因,我使用了表变量,无需为了示例而创建“真实”表。

公用表表达式 (CTE) 通过递归创建覆盖所需范围的 QuarterHours 表。 (numbers tabletally table 也可用于生成刻钟。)然后使用带有示例数据的 LEFT OUTER JOIN 来定位所有 @987654329 @,如果有,每刻钟。这会保留没有活动的一刻钟。

最后的SELECT总结了结果。

【讨论】:

  • 这比我想象或希望的要复杂得多。我可能需要一些时间来理解所有内容的含义。
  • 我还没有打算发送我的最后一条评论。整件事是这样的:这比我想象或希望的要复杂得多。我可能需要一些时间来理解所有内容的含义。在您插入示例数据的地方,是否可以插入我的开始和停止时间变量而不是显式值?如果这些是简单的问题,我深表歉意,但正如我所说,我对 SQL 相当陌生,这将是我使用过的最复杂的查询。非常感谢您的帮助!
  • @William - 我添加了一些额外的注释来解释发生了什么。同样,如果您将最终的 select 更改为显示 QuarterHoursOverlaps 表,它应该会更清晰。
  • 非常感谢您的澄清说明!
  • 我想写一个简短的后续。您编写的查询非常适合我的需要。再次感谢您的帮助。
【解决方案2】:

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

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 );

返回这个:

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 分钟增量一次,无论是否有任何活动在其中开始。所以我们必须使用一个计数表来确保生成每个间隔,如果没有别的,从前一个间隔捕获一些溢出分钟。

然后,当然,有溢出跟踪运行总数。虽然这是可能的(请参阅https://stackoverflow.com/a/861073/3658753 以获得关于如何进行的很好的解释),但让我感到震惊的是,两者(计数表和运行总计)的组合是在 SQL 中执行的大量开销。请记住,在 SQL 中执行计算甚至比最快的磁盘访问要快很多倍,但是在任何高级语言(Java、C++、C# 甚至像 Perl 这样的脚本语言)中执行计算都将比 SQL 快很多倍。此外,SQL 解决方案的可维护性将深入地下室。

因此,此时我的建议是采用上述查询并将其输入到一个好的报告引擎或您的应用程序中,并让它们执行额外的计算。在性能方面,您将遥遥领先。

【讨论】:

    猜你喜欢
    • 2015-05-02
    • 2020-04-10
    • 2021-10-26
    • 2021-04-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-24
    • 1970-01-01
    相关资源
    最近更新 更多