You need to enable JavaScript to run this app. SQL Server 基于缺勤记录起止日期拆分行并合理分配损失班次字段
优惠活动
大模型
产品
解决方案
定价
更多

SQL Server 基于缺勤记录起止日期拆分行并合理分配损失班次字段

可实现性说明

完全可以完成两项核心处理需求:一是将跨周期的单条缺勤记录,拆分为覆盖周期内每个自然日的独立行;二是拆分累计损失班次字段,避免原值全量重复复制到每一行导致的统计冗余。

基础拆分:生成单自然日缺勤记录

实现逻辑为构建连续日期序列,与原缺勤表做匹配,当序列日期落在原记录的Start Date到End Date闭区间内时,生成对应单天的独立记录。

  • 兼容所有SQL Server版本的递归CTE实现(无版本依赖):
-- 源数据替换为实际的员工缺勤表
WITH AbsenceRaw AS (
    SELECT
        PersonRef,
        StartDate,
        EndDate,
        ShiftsLost
    FROM dbo.EmployeeAbsenceRecords
),
DateSeq AS (
    -- 递归从每个缺勤记录的开始日期生成连续日期,直到匹配到结束日期
    SELECT
        PersonRef,
        StartDate AS SingleDay,
        EndDate,
        ShiftsLost
    FROM AbsenceRaw
    UNION ALL
    SELECT
        PersonRef,
        DATEADD(DAY, 1, SingleDay) AS SingleDay,
        EndDate,
        ShiftsLost
    FROM DateSeq
    WHERE SingleDay < EndDate
)
SELECT
    PersonRef,
    SingleDay AS StartDate,
    SingleDay AS EndDate,
    ShiftsLost
FROM DateSeq
-- 取消递归层级限制,避免长周期缺勤触发默认100层的递归上限报错
OPTION (MAXRECURSION 0)
  • SQL Server 2022及以上版本的高效实现(无递归性能损耗):
SELECT
    r.PersonRef,
    DATEADD(DAY, gs.step, r.StartDate) AS StartDate,
    DATEADD(DAY, gs.step, r.StartDate) AS EndDate,
    r.ShiftsLost
FROM dbo.EmployeeAbsenceRecords r
-- 按缺勤周期的天数生成从0开始的连续步长
CROSS APPLY GENERATE_SERIES(0, DATEDIFF(DAY, r.StartDate, r.EndDate)) gs
损失班次的合理分配

你当前遇到的重复赋值问题,本质是拆分时直接将原行的累计损失值透传到了每一行,没有做拆分计算,会导致后续统计时同一个累计值被重复累加N次,结果严重虚高。
针对你示例中“累计3个损失班次,优先分配给周期内靠前的3个自然日,剩余日期记0”的规则,可在拆分日期的基础上增加排序逻辑,实现精准分配:

WITH AbsenceRaw AS (
    SELECT
        PersonRef,
        StartDate,
        EndDate,
        ShiftsLost
    FROM dbo.EmployeeAbsenceRecords
),
DateSeq AS (
    SELECT
        PersonRef,
        StartDate AS SingleDay,
        EndDate,
        ShiftsLost
    FROM AbsenceRaw
    UNION ALL
    SELECT
        PersonRef,
        DATEADD(DAY, 1, SingleDay) AS SingleDay,
        EndDate,
        ShiftsLost
    FROM DateSeq
    WHERE SingleDay < EndDate
),
DaysRanked AS (
    -- 同一缺勤周期内,按日期从早到晚排序
    SELECT
        *,
        ROW_NUMBER() OVER (
            PARTITION BY PersonRef, StartDate, EndDate
            ORDER BY SingleDay ASC
        ) AS DayOrderInAbsence
    FROM DateSeq
)
SELECT
    PersonRef,
    SingleDay AS StartDate,
    SingleDay AS EndDate,
    -- 核心分配逻辑:当日在周期内的排序小于等于总损失数,分配1个班次,否则记0
    CASE
        WHEN DayOrderInAbsence <= ShiftsLost THEN 1
        ELSE 0
    END AS ShiftsLost
FROM DaysRanked
OPTION (MAXRECURSION 0)

实际生产场景提示:如果企业有固定排班规则,不要硬编码按日期先后分配,建议拆分完成后关联员工排班表,按照当日实际排班的班次数值对应赋值损失班次,只要保证所有拆分日的损失值总和等于原记录的累计值即可,这种方式更贴合实际业务逻辑。如果存在单日多班的情况,只需将CASE判断中的固定赋值1替换为当日实际排班数,无需改动整体拆分框架。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.26 08:36:04