You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SQL Server 2014窗口查询:拆分跨多30分钟间隔的记录为单行

这个需求在日志分析、工时统计这类场景里特别实用,我来给你分享几种主流数据库下的实现方案,核心思路都是先生成覆盖目标时间范围的30分钟间隔序列,再计算每个间隔内的实际有效时长:

MySQL 实现示例

假设你的数据存在time_records表中,包含id、start_time、end_time字段。我们可以用递归CTE生成所需的时间间隔:

WITH RECURSIVE time_intervals AS (
    -- 初始行:把原记录的开始时间向下取整到最近的30分钟节点
    SELECT 
        id,
        start_time,
        end_time,
        DATE_FORMAT(start_time, '%Y-%m-%d %H:00:00') + INTERVAL (FLOOR(MINUTE(start_time)/30)*30) MINUTE AS interval_start
    FROM time_records
    WHERE id = 1 -- 替换成你要查询的记录ID,去掉WHERE可处理全表记录
    UNION ALL
    -- 递归生成后续的30分钟间隔,直到覆盖结束时间的向上取整节点
    SELECT 
        id,
        start_time,
        end_time,
        interval_start + INTERVAL 30 MINUTE
    FROM time_intervals
    WHERE interval_start + INTERVAL 30 MINUTE <= DATE_FORMAT(end_time, '%Y-%m-%d %H:00:00') + INTERVAL (CEIL(MINUTE(end_time)/30)*30) MINUTE
)
-- 计算每个间隔的实际有效起止时间和耗时
SELECT 
    id,
    interval_start,
    interval_start + INTERVAL 30 MINUTE AS interval_end,
    GREATEST(interval_start, start_time) AS actual_start, -- 取间隔开始和原记录开始的较大值
    LEAST(interval_start + INTERVAL 30 MINUTE, end_time) AS actual_end, -- 取间隔结束和原记录结束的较小值
    TIMESTAMPDIFF(SECOND, GREATEST(interval_start, start_time), LEAST(interval_start + INTERVAL 30 MINUTE, end_time)) AS duration_seconds
FROM time_intervals
ORDER BY interval_start;
PostgreSQL 实现示例

PostgreSQL自带的generate_series函数可以更简洁地生成时间序列,省去递归步骤:

WITH target_record AS (
    -- 这里替换成你的目标记录,或者直接关联原表
    SELECT 
        '2024-01-01 02:01:37'::timestamp AS start_time,
        '2024-01-01 05:00:21'::timestamp AS end_time
)
SELECT 
    ts AS interval_start,
    ts + INTERVAL '30 minutes' AS interval_end,
    GREATEST(ts, start_time) AS actual_start,
    LEAST(ts + INTERVAL '30 minutes', end_time) AS actual_end,
    EXTRACT(EPOCH FROM (LEAST(ts + INTERVAL '30 minutes', end_time) - GREATEST(ts, start_time))) AS duration_seconds
FROM target_record,
     generate_series(
         -- 生成从原开始时间向下取整的30分钟节点
         date_trunc('hour', start_time) + INTERVAL '30 minutes' * FLOOR(date_part('minute', start_time)/30),
         -- 到原结束时间向上取整的30分钟节点
         date_trunc('hour', end_time) + INTERVAL '30 minutes' * CEIL(date_part('minute', end_time)/30),
         -- 步长30分钟
         INTERVAL '30 minutes'
     ) AS ts
ORDER BY ts;
SQL Server 实现示例

如果是SQL Server,2022及以上版本可以用GENERATE_SERIES,旧版本则用递归CTE:

-- 递归CTE版本(兼容所有SQL Server版本)
WITH time_intervals AS (
    SELECT 
        start_time,
        end_time,
        -- 把开始时间向下取整到最近的30分钟节点
        DATEADD(MINUTE, DATEDIFF(MINUTE, 0, start_time)/30*30, 0) AS interval_start
    FROM (
        -- 替换成你的目标记录或原表
        SELECT CAST('2024-01-01 02:01:37' AS datetime) AS start_time,
               CAST('2024-01-01 05:00:21' AS datetime) AS end_time
    ) AS t
    UNION ALL
    SELECT 
        start_time,
        end_time,
        DATEADD(MINUTE, 30, interval_start)
    FROM time_intervals
    -- 递归直到覆盖结束时间的向上取整节点
    WHERE DATEADD(MINUTE, 30, interval_start) <= DATEADD(MINUTE, CEILING(DATEDIFF(MINUTE, 0, end_time)/30.0)*30, 0)
)
SELECT 
    interval_start,
    DATEADD(MINUTE, 30, interval_start) AS interval_end,
    GREATEST(interval_start, start_time) AS actual_start,
    LEAST(DATEADD(MINUTE, 30, interval_start), end_time) AS actual_end,
    DATEDIFF(SECOND, GREATEST(interval_start, start_time), LEAST(DATEADD(MINUTE, 30, interval_start), end_time)) AS duration_seconds
FROM time_intervals
ORDER BY interval_start
OPTION (MAXRECURSION 0); -- 如果间隔数量超过100,需要开启这个选项

补充说明

  • 如果要处理表中所有记录,只需要调整CTE的初始查询部分,去掉WHERE条件直接关联原表即可。
  • actual_start和actual_end是该30分钟间隔内实际有记录的时间段,duration_seconds就是这个间隔内的耗时,你可以根据需求转换成分钟、小时等单位。
  • 不同数据库的时间函数略有差异,但核心逻辑完全一致:先生成覆盖目标范围的30分钟间隔序列,再计算每个间隔的有效时间区间。

内容的提问来源于stack exchange,提问作者Mary

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 04:00:13