SQL如何在时间段内分组

Posted

技术标签:

【中文标题】SQL如何在时间段内分组【英文标题】:SQL how to group in time periods 【发布时间】:2011-10-18 02:23:32 【问题描述】:

我正在尝试按时间段对数据进行分组。每个时间段为 5 分钟,我想看看从 08:00 到 18:00 每 5 分钟发生的情况。

我创建了一个表格,其中包含该时间范围内的所有时间段。例如:

StartTime           EndTime             IsBusinessHours
08:40:00.0000000    08:45:00.0000000    1
08:45:00.0000000    08:50:00.0000000    1
08:50:00.0000000    08:55:00.0000000    1
08:55:00.0000000    09:00:00.0000000    1

等等

Select 
    TimeDimension.[StartTime],
    TimeDimension.[EndTime],
    activity.[Description],
    activity.[StartTime]
From
    TimeDimension 
    Full Outer Join Activity 
       on (
              Convert(varchar,activity.StartTime,108) >= Convert(varchar,TimeDimension.starttime, 108) 
          And Convert(varchar,activity.StartTime,108) <= Convert(varchar,TimeDimension.endtime, 108)
       )
Where               
    activity.Date = @DateParam 
And TimeDimension.isbusinesshours = 1

我希望数据按 5 分钟时间段分组,但我得到的是:

08:20:00.0000000    08:25:00.0000000 Some activity
08:30:00.0000000    08:35:00.0000000 Some activity
08:45:00.0000000    08:50:00.0000000 Three activities in this time period. First
08:45:00.0000000    08:50:00.0000000 Three activities in this time period. Second 
08:45:00.0000000    08:50:00.0000000 Three activities in this time period. Third

当我想看到的是:

08:20:00.0000000    08:25:00.0000000 Some activity
08:25:00.0000000    08:30:00.0000000 NULL
08:30:00.0000000    08:35:00.0000000 Some activity
08:35:00.0000000    08:40:00.0000000 NULL
08:45:00.0000000    08:50:00.0000000 Three activities in this time period. First
08:45:00.0000000    08:50:00.0000000 Three activities in this time period. Second 
08:45:00.0000000    08:50:00.0000000 Three activities in this time period. Third

这意味着我显示的是发生某些活动的时间段,而不是该范围内的所有时间段。我已经调用了一个表 TimeDimension - 但我不确定这是否正确。直觉告诉我,这与分析服务有关。

谢谢

【问题讨论】:

我认为这只是意味着您在 08:45 到 08:50 之间有 4 个活动。你想输出什么? 对不起,5 分钟。请看更新 【参考方案1】:

注意 1:使用 VARCHAR 进行 DATETIME 算术会产生较差的性能。

注意 2:您有一个 OUTER JOIN,但有一个不考虑 NULL 的 WHERE 子句。

这就是我要使用的...

WITH
  FilteredActivity AS
(
  SELECT
    Description,
    DATEADD(DAY, -DATEDIFF(DAY, 0, StartTime), StartTime) AS StartTime
  FROM
    Activity
  WHERE
    Date = @DateParam
)

SELECT
  TimeDimension.[StartTime],
  TimeDimension.[EndTime],
  activity.[Description],
  activity.[StartTime]
FROM
  TimeDimension
LEFT JOIN
  FilteredActivity AS [Activity]
    ON  Activity.StartTime >= TimeDimension.StartTime
    AND Activity.StartTime <  TimeDimension.EndTime
WHERE
  TimeDimension.isbusinesshours = 1

CTE 过滤

一开始的 CTE 将活动过滤到一个日期 这避免了 WHERE 子句中与 OUTER JOIN 配合不佳的条件

CTE 格式

CTE 还将 StartTime 剥离为仅一个 TimePart 仅当 StartTime 也包含 DATE 时才需要 DATEADD/DATEDIFF 业务 如果时间已经到了,就用 StartTime

独占与包容的 EndTime

我有&lt; EndTime 而不是&lt;= EndTime 这假设间隔为08:00 to 08:0508:05 to 08:10 等形式 将 EndTime 设置为“您不想包含的第一次”可以让事情变得更容易 不再将 Activity.StartTime 向下舍入到最接近的分钟,例如 并且没有08:00 to 08:04等奇怪的间隔

使用独占 EndTime 值的替代方法是将 Activity.StartTime 值四舍五入到最接近的分钟。以下不是使用字符串,而是使用 DateTime 函数... - DATEADD(minute, DATEDIFF(minute, 0, Activity.StartTime), 0)

【讨论】:

非常感谢德姆斯。我以前从未使用过常用的表达式表 :)【参考方案2】:

您说您想对结果进行分组,但您没有将GROUP 应用于查询。

但是,如果您汇总结果,您将丢失您也想要的独特信息(DescriptionStartDate),除非它们与组中的其他记录匹配。

正如 Scorpi0 所评论的,您想要的输出样本会很有用。

【讨论】:

因此您实际上并不需要GROUP BY 查询,您希望查看所有时间范围,即使没有任何活动。因此,似乎 Scorpi0 的答案会为您提供所需的东西。但是,当每个“时间组”多于一行时会发生什么? 嗨托尼。是的,我想查看一段时间内的所有活动。如果有不止一个活动,那么我希望在每一行中看到所有活动和重复的时间段。【参考方案3】:

您在活动表上有一个过滤器:activity.Date = @DateParam

它会阻止获取 TimeDimension 表的每一行。将过滤器放在连接子句中,您将看到所有数据。

Select 
    TimeDimension.[StartTime],
    TimeDimension.[EndTime],
    activity.[Description],
    activity.[StartTime]
From
    TimeDimension 
    Full Outer Join Activity 
       on (
              Convert(varchar,activity.StartTime,108) >= Convert(varchar,TimeDimension.starttime, 108) 
          And Convert(varchar,activity.StartTime,108) <= Convert(varchar,TimeDimension.endtime, 108)
          And activity.Date = @DateParam 
       )
Where TimeDimension.isbusinesshours = 1

或者你也可以这样做:

Select 
    TimeDimension.[StartTime],
    TimeDimension.[EndTime],
    activity.[Description],
    activity.[StartTime]
From
    TimeDimension 
    Full Outer Join Activity 
       on (
              Convert(varchar,activity.StartTime,108) >= Convert(varchar,TimeDimension.starttime, 108) 
          And Convert(varchar,activity.StartTime,108) <= Convert(varchar,TimeDimension.endtime, 108)
       )
Where TimeDimension.isbusinesshours = 1
And (activity.Date Is Null Or activity.Date = @DateParam)

【讨论】:

您好,感谢您的回复。我已经进行了更改,并且得到了相同的结果... 看起来第一个查询应该可以解决问题。你确定不是吗,vikp?【参考方案4】:

您需要将活动时间条件从常见的 Where 子句移到 Join 条件中,如下所示:

Select 
    TimeDimension.[StartTime],
    TimeDimension.[EndTime],
    activity.[Description],
    activity.[StartTime]
From
    TimeDimension 
    Full Outer Join Activity 
       on (
              Convert(varchar,activity.StartTime,108) >= Convert(varchar,TimeDimension.starttime, 108) 
          And Convert(varchar,activity.StartTime,108) <= Convert(varchar,TimeDimension.endtime, 108)
And activity.Date = @DateParam
       )
Where               
    TimeDimension.isbusinesshours = 1

【讨论】:

"A >= B and A

以上是关于SQL如何在时间段内分组的主要内容,如果未能解决你的问题,请参考以下文章

BigQuery:如何在滚动时间戳窗口内对行进行分组和计数?

sql语句 在分组内排序

SQL Case 语句子选择内的子选择分组

如何按 id 对数据进行分组并使用 SQL 获取中值?

sql 分组排序

如何使用 sql 和按日期分组显示指标在日期上的累积增长?