You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

多打卡记录生成员工班次的SQL查询:解决跨夜班次合并问题

解决跨夜班次的打卡记录合并问题

我来帮你搞定这个跨天班次的打卡合并问题——你的原有查询在处理跨夜班次时失效,核心原因是按打卡日期分组的逻辑不符合实际班次的连续性,下面是具体的解决方案和细节解释:

问题根源分析

你原来的查询语句是按CAST(PUNCHIN AS DATE)分组,这个逻辑在处理不跨天的白班时没问题,但遇到跨夜班次就会“拆碎”同一个班次:比如员工DGHAG从2020-07-10 20:00开始上班,后续的打卡记录(比如2020-07-11 01:45的PUNCHIN)日期是7月11号,会被分到另一个日期组,导致同一个完整班次被拆成多条记录,自然无法得到正确的合并结果。

解决方案:按连续打卡记录分组

我们需要基于「同班次的打卡记录是连续的(上一条的PUNCHOUT等于当前的PUNCHIN)」这个规则来分组,用窗口函数就能实现这个逻辑:

完整SQL查询(适配SQL Server)

WITH ShiftGroups AS (
    SELECT 
        EMPLOYEEID,
        PUNCHIN,
        PUNCHOUT,
        -- 标记是否为新班次:如果当前PUNCHIN和上一条的PUNCHOUT不相等,说明是新班次
        CASE 
            WHEN LAG(PUNCHOUT) OVER (PARTITION BY EMPLOYEEID ORDER BY PUNCHIN) = PUNCHIN 
            THEN 0 
            ELSE 1 
        END AS IsNewShift,
        -- 累计新班次标记,生成每个班次的唯一分组ID
        SUM(CASE 
                WHEN LAG(PUNCHOUT) OVER (PARTITION BY EMPLOYEEID ORDER BY PUNCHIN) = PUNCHIN 
                THEN 0 
                ELSE 1 
            END) OVER (PARTITION BY EMPLOYEEID ORDER BY PUNCHIN ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS ShiftGroupID
    FROM EMPSCHED
    -- 先排除无效的打卡(PUNCHIN=PUNCHOUT),这些是结束标记,聚合时会被自动包含
    WHERE PUNCHIN <> PUNCHOUT
)
SELECT 
    EMPLOYEEID,
    MIN(PUNCHIN) AS ShiftStart,  -- 班次的首次打卡时间
    MAX(PUNCHOUT) AS ShiftEnd    -- 班次的末次打卡时间
FROM ShiftGroups
GROUP BY EMPLOYEEID, ShiftGroupID
ORDER BY EMPLOYEEID, ShiftStart;

逻辑拆解

  1. CTE ShiftGroups 部分

    • LAG(PUNCHOUT) OVER (...):获取当前员工上一条打卡记录的离开时间,用来判断是否和当前记录属于同一个班次。
    • IsNewShift:如果当前记录的PUNCHIN和上一条的PUNCHOUT不相等,说明这是新班次的开始,标记为1;否则属于同一个班次,标记为0。
    • ShiftGroupID:通过累计IsNewShift的值,为每个连续的班次生成唯一的分组ID——同一个班次的所有记录会拥有相同的ID。
  2. 最终聚合部分
    按员工ID和班次分组ID聚合,取分组内最早的PUNCHIN作为班次开始时间,最晚的PUNCHOUT作为班次结束时间,完美合并同班次的所有记录。

示例结果验证

针对你提供的示例数据,运行上述查询后:

  • DGHAG的跨夜班次会被正确合并:
    • 2020-07-10 20:00:00 → 2020-07-11 06:30:00(合并4条连续记录)
    • 2020-07-11 20:00:00 → 2020-07-12 06:30:00(合并4条连续记录)
  • DGHFA的白班记录也会合并为每天一条(比如2020-07-08 08:30:00 → 13:30:00)

补充说明

  • 如果你需要保留PUNCHIN=PUNCHOUT的记录(比如作为班次结束的标记),可以去掉WHERE PUNCHIN <> PUNCHOUT,聚合时MAX(PUNCHOUT)会自动取到最后那个结束标记的时间,不影响结果。
  • 这个方案适配SQL Server(你的建表语句是SQL Server风格),如果是其他数据库,语法可能略有调整,但核心逻辑完全通用。

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

火山引擎 最新活动