SQL实现:从含汇总时段数据的源表t_times生成拆分后的时段表t_slots
嘿,这个需求完全不用麻烦游标!咱们用集合式SQL就能轻松搞定,效率还高得多。核心思路是先生成对应数量的数字序列,再基于原表的时间和间隔计算每个子时段的起止时间。下面分几种主流数据库给你具体的实现方案:
PostgreSQL 实现
PostgreSQL自带的generate_series函数简直是为这种场景量身定做的,代码简洁又高效:
INSERT INTO t_slots (start_time, end_time, duration) SELECT -- 计算子时段开始时间:原起始时间加上(n-1)个间隔时长 t.start_time + (s.n - 1) * INTERVAL '1 minute' * t.slot_duration AS start_time, -- 子时段结束时间就是开始时间加一个间隔 t.start_time + s.n * INTERVAL '1 minute' * t.slot_duration AS end_time, t.slot_duration AS duration FROM t_times t -- 为每条原记录生成1到number_of_slots的数字序列,对应要生成的行数 CROSS JOIN generate_series(1, t.number_of_slots) s(n);
generate_series(1, t.number_of_slots)会自动为t_times里的每条记录生成对应数量的数字行,再通过时间运算直接算出每个子时段的起止。
MySQL 8.0+ 实现
MySQL 8.0及以上支持递归CTE,我们可以用它生成足够多的数字序列,再和原表关联:
WITH RECURSIVE nums AS ( SELECT 1 AS n UNION ALL SELECT n + 1 FROM nums WHERE n < (SELECT MAX(number_of_slots) FROM t_times) ) INSERT INTO t_slots (start_time, end_time, duration) SELECT ADDTIME(t.start_time, SEC_TO_TIME((n - 1) * t.slot_duration * 60)) AS start_time, ADDTIME(t.start_time, SEC_TO_TIME(n * t.slot_duration * 60)) AS end_time, t.slot_duration AS duration FROM t_times t JOIN nums ON nums.n <= t.number_of_slots;
先递归生成从1到t_times中最大number_of_slots的数字序列,再通过JOIN只保留每条原记录需要的行数,最后用ADDTIME和SEC_TO_TIME计算时间。
SQL Server 实现
SQL Server同样可以用递归CTE生成数字序列,配合DATEADD函数计算时间:
WITH nums AS ( SELECT 1 AS n UNION ALL SELECT n + 1 FROM nums WHERE n < (SELECT MAX(number_of_slots) FROM t_times) ) INSERT INTO t_slots (start_time, end_time, duration) SELECT DATEADD(MINUTE, (n - 1) * t.slot_duration, t.start_time) AS start_time, DATEADD(MINUTE, n * t.slot_duration, t.start_time) AS end_time, t.slot_duration AS duration FROM t_times t JOIN nums ON nums.n <= t.number_of_slots OPTION (MAXRECURSION 0); -- 如果number_of_slots超过100,必须加上这个选项
递归生成数字序列后,用DATEADD直接给原起始时间加上对应分钟数得到子时段。OPTION (MAXRECURSION 0)是为了避免默认递归次数(100次)不够的问题。
通用思路总结
不管用哪种数据库,核心逻辑都是这三步:
- 生成一个数字序列,覆盖从1到每条记录
number_of_slots的范围 - 将原表和数字序列关联,得到对应数量的行
- 计算每个子时段的时间:
- 子时段开始时间 = 原
start_time+ (n-1)*slot_duration分钟 - 子时段结束时间 = 原
start_time+ n*slot_duration分钟 duration直接复用原表的slot_duration
- 子时段开始时间 = 原
这种集合式操作比游标高效太多,数据库优化器能更好地处理批量数据,代码也更简洁易维护。
内容的提问来源于stack exchange,提问作者James F
相关产品推荐
相关产品推荐

