如何用MS SQL计算两个时间点在半小时区间的时长分布?
MS SQL按半小时区间统计时长的实现方案
核心思路
不需要用循环处理,SQL是基于集合的语言,通过预生成所有半小时区间,再将目标时间范围与每个区间计算交集时长,是最高效的处理方式。
单条时间范围的统计示例
假设已知起始时间@start_time = '08:47',结束时间@end_time = '09:07',可以用以下代码直接输出各区间的时长:
DECLARE @start_time time = '08:47', @end_time time = '09:07'; WITH HalfHourIntervals AS ( -- 生成一天内所有48个半小时区间 SELECT DATEADD(minute, 30 * n, CAST('00:00' AS time)) AS IntervalStart, DATEADD(minute, 30 * (n + 1), CAST('00:00' AS time)) AS IntervalEnd FROM ( -- 生成0到47的数字序列,对应48个区间 SELECT TOP 48 ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) - 1 AS n FROM sys.all_columns -- 利用系统表生成数字,也可以用自定义数字表 ) t ) SELECT CONVERT(varchar(5), IntervalStart) + ' - ' + CONVERT(varchar(5), IntervalEnd) AS TimeInterval, -- 计算当前区间与目标时间范围的交集时长 DATEDIFF(minute, MAX(@start_time, IntervalStart), MIN(@end_time, IntervalEnd) ) AS DurationMinutes FROM HalfHourIntervals -- 过滤出与目标时间范围有重叠的区间 WHERE IntervalStart < @end_time AND IntervalEnd > @start_time ORDER BY IntervalStart;
执行后会得到预期结果:
TimeInterval | DurationMinutes -------------|----------------- 08:30 - 09:00| 13 09:00 - 09:30| 7
批量时间记录的统计方案
如果是处理表中的批量时间记录(比如表TimeRecords包含id、start_time、end_time字段),只需将CTE生成的区间与表做交叉连接,再计算每条记录的区间时长:
WITH HalfHourIntervals AS ( SELECT DATEADD(minute, 30 * n, CAST('00:00' AS time)) AS IntervalStart, DATEADD(minute, 30 * (n + 1), CAST('00:00' AS time)) AS IntervalEnd FROM ( SELECT TOP 48 ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) - 1 AS n FROM sys.all_columns ) t ) SELECT tr.id, CONVERT(varchar(5), hhi.IntervalStart) + ' - ' + CONVERT(varchar(5), hhi.IntervalEnd) AS TimeInterval, DATEDIFF(minute, MAX(tr.start_time, hhi.IntervalStart), MIN(tr.end_time, hhi.IntervalEnd) ) AS DurationMinutes FROM TimeRecords tr CROSS JOIN HalfHourIntervals hhi WHERE hhi.IntervalStart < tr.end_time AND hhi.IntervalEnd > tr.start_time ORDER BY tr.id, hhi.IntervalStart;
为什么不推荐用循环
循环属于逐行处理的方式,在MS SQL中面对大量数据时性能极差,远不如基于集合的操作高效。预生成区间的方式只需一次生成所有可能的区间,再通过集合运算完成统计,无论是执行速度还是维护性都更优。
内容的提问来源于stack exchange,提问作者RustyStupidSQL
相关产品推荐
相关产品推荐

