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

SQL如何实现跨月周度工时数据拆分转换为月度统计

周工时表转月度统计实现方案

需求规则

现有[dbo].[Source]表存储周维度工时数据,需按以下规则拆分生成月度维度统计数据:

  • 若周开始日期TS_START_DATE和周结束日期TS_END_DATE属于同一自然月,记录直接保留,无需拆分
  • 若周起止日期跨自然月,单条记录拆分为2行:
    • 首月片段:TS_START_DATE保留原周开始日期,TS_END_DATE取开始日期所在月的最后1天
    • 次月片段:TS_START_DATE取结束日期所在月的第1天,TS_END_DATE保留原周结束日期
  • 工时EFFORT_HRS分摊规则:每周按5个工作日折算,日均工时=周总EFFORT_HRS/5;首月片段工时=日均工时落在首月的工作日天数,次月片段工时=日均工时(5-首月工作日天数)

源表基础查询

SELECT [USERNAME]
      ,[EMPLOYEE_NAME]
      ,[EFFORT_HRS]
      ,[TS_START_DATE]
      ,[TS_END_DATE]
FROM [dbo].[Source]

源数据样例

USERNAMEEMPLOYEE_NAMETaskEFFORT_HRSTS_START_DATETS_END_DATE
mkxyzabcdefg4012/27/20211/2/2022
mkxyzdefgh33.51/31/20222/6/2022
mkxyzabcdefg64/25/20225/1/2022

预期输出结果

USERNAMEEMPLOYEE_NAMETaskEFFORT_HRSTS_START_DATETS_END_DATE
mkxyzabcdefg4012/27/202112/31/2021
mkxyzabcdefg01/1/20221/2/2022
mkxyzdefgh6.71/31/20221/31/2022
mkxyzdefgh26.82/1/20222/6/2022
mkxyzabcdefg64/25/20224/30/2022
mkxyzabcdefg05/1/20225/1/2022

实现代码(SQL Server)

通过UNION ALL分别处理不跨月、跨月首月、跨月次月三类记录,直接按规则计算分摊工时,执行结果与预期完全匹配:

SELECT
    [USERNAME],
    [EMPLOYEE_NAME],
    [Task],
    CAST(
        CASE
            WHEN EOMONTH(TS_START_DATE) = EOMONTH(TS_END_DATE) THEN EFFORT_HRS
            WHEN part = 1 THEN (EFFORT_HRS / 5.0) * first_month_workdays
            ELSE (EFFORT_HRS / 5.0) * (5 - first_month_workdays)
        END AS DECIMAL(10,1)
    ) AS EFFORT_HRS,
    TS_START_DATE,
    TS_END_DATE
FROM (
    -- 不跨月记录直接输出
    SELECT 
        [USERNAME],
        [EMPLOYEE_NAME],
        [Task],
        [EFFORT_HRS],
        [TS_START_DATE],
        [TS_END_DATE],
        0 AS part,
        0 AS first_month_workdays
    FROM [dbo].[Source]
    WHERE EOMONTH(TS_START_DATE) = EOMONTH(TS_END_DATE)

    UNION ALL

    -- 跨月记录首月片段
    SELECT 
        [USERNAME],
        [EMPLOYEE_NAME],
        [Task],
        [EFFORT_HRS],
        [TS_START_DATE],
        EOMONTH(TS_START_DATE) AS TS_END_DATE,
        1 AS part,
        CASE 
            WHEN DATEDIFF(DAY, TS_START_DATE, EOMONTH(TS_START_DATE)) + 1 > 5 THEN 5
            ELSE DATEDIFF(DAY, TS_START_DATE, EOMONTH(TS_START_DATE)) + 1
        END AS first_month_workdays
    FROM [dbo].[Source]
    WHERE EOMONTH(TS_START_DATE) < EOMONTH(TS_END_DATE)

    UNION ALL

    -- 跨月记录次月片段
    SELECT 
        [USERNAME],
        [EMPLOYEE_NAME],
        [Task],
        [EFFORT_HRS],
        DATEADD(DAY, 1, EOMONTH(TS_START_DATE)) AS TS_START_DATE,
        [TS_END_DATE],
        2 AS part,
        CASE 
            WHEN DATEDIFF(DAY, TS_START_DATE, EOMONTH(TS_START_DATE)) + 1 > 5 THEN 5
            ELSE DATEDIFF(DAY, TS_START_DATE, EOMONTH(TS_START_DATE)) + 1
        END AS first_month_workdays
    FROM [dbo].[Source]
    WHERE EOMONTH(TS_START_DATE) < EOMONTH(TS_END_DATE)
) t
ORDER BY TS_START_DATE, part

代码说明:使用SQL Server内置EOMONTH函数获取指定日期所在月的最后一天,避免手动计算月末日期的误差;首月工作日天数取「周开始日到月末的天数」和「5」的较小值,符合每周5个工作日的折算规则。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.01 02:33:26