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

如何计算流程起止的非周末有效工作时长(小时)?

计算流程非周末时长(小时)的解决方案

第一步:提取每个流程的起止时间

首先从表1中拆分出每个deal的开始、结束时间(假设status_id=1代表流程启动,status_id=2代表流程结束):

WITH deal_time AS (
    SELECT 
        deal_id,
        MAX(CASE WHEN status_id = 1 THEN created_at END) AS start_time,
        MAX(CASE WHEN status_id = 2 THEN created_at END) AS end_time
    FROM table1
    GROUP BY deal_id
    HAVING start_time IS NOT NULL AND end_time IS NOT NULL
)

第二步:计算非周末时长

通过生成起止时间范围内的所有日期,关联表2标记周末,再分别计算各时间段的周末时长,最终用总时长扣除周末时长得到结果:

SELECT 
    dt.deal_id,
    -- 计算总时长(小时)
    EXTRACT(EPOCH FROM (dt.end_time - dt.start_time)) / 3600 AS total_hours,
    -- 计算周末总时长(小时)
    SUM(
        CASE 
            -- 起止时间在同一天且当天是周末
            WHEN dt.start_time::DATE = dt.end_time::DATE AND t.type = 'hol' 
            THEN EXTRACT(EPOCH FROM (dt.end_time - dt.start_time)) / 3600
            -- 开始当天是周末,计算从开始到当天结束的时长
            WHEN date_range.dt = dt.start_time::DATE AND t.type = 'hol'
            THEN EXTRACT(EPOCH FROM (DATE_TRUNC('day', dt.start_time) + INTERVAL '1 day' - dt.start_time)) / 3600
            -- 结束当天是周末,计算从当天开始到结束的时长
            WHEN date_range.dt = dt.end_time::DATE AND t.type = 'hol'
            THEN EXTRACT(EPOCH FROM (dt.end_time - DATE_TRUNC('day', dt.end_time))) / 3600
            -- 中间完整的周末日期,按24小时计算
            WHEN t.type = 'hol'
            THEN 24
            ELSE 0
        END
    ) AS weekend_hours,
    -- 非周末时长 = 总时长 - 周末时长
    (EXTRACT(EPOCH FROM (dt.end_time - dt.start_time)) / 3600) - 
    SUM(
        CASE 
            WHEN dt.start_time::DATE = dt.end_time::DATE AND t.type = 'hol' 
            THEN EXTRACT(EPOCH FROM (dt.end_time - dt.start_time)) / 3600
            WHEN date_range.dt = dt.start_time::DATE AND t.type = 'hol'
            THEN EXTRACT(EPOCH FROM (DATE_TRUNC('day', dt.start_time) + INTERVAL '1 day' - dt.start_time)) / 3600
            WHEN date_range.dt = dt.end_time::DATE AND t.type = 'hol'
            THEN EXTRACT(EPOCH FROM (dt.end_time - DATE_TRUNC('day', dt.end_time))) / 3600
            WHEN t.type = 'hol'
            THEN 24
            ELSE 0
        END
    ) AS non_weekend_hours
FROM deal_time dt
-- 生成起止时间之间的所有日期
CROSS JOIN LATERAL (
    SELECT generate_series(
        dt.start_time::DATE, 
        dt.end_time::DATE, 
        INTERVAL '1 day'
    )::DATE AS dt
) date_range
-- 关联表2判断日期类型
LEFT JOIN table2 t ON date_range.dt = t.dt
GROUP BY dt.deal_id, dt.start_time, dt.end_time;

适配不同SQL方言

如果使用MySQL(不支持LATERAL和generate_series),可以用递归CTE生成日期范围:

WITH RECURSIVE date_range AS (
    SELECT start_time::DATE AS dt, end_time, deal_id
    FROM deal_time
    UNION ALL
    SELECT dt + INTERVAL 1 DAY, end_time, deal_id
    FROM date_range
    WHERE dt < end_time::DATE
)

替换原SQL中的CROSS JOIN LATERAL部分即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 13:30:09