窗口函数通过ROW_NUMBER()与日期序号差值构造连续组ID来识别连续打卡/缺勤段,用LAG()/LEAD()跨行比对班次衔接,RANK()与ROW_NUMBER()依并列需求选用于排班排序,MySQL 5.7需用变量或应用层替代。

窗口函数怎么处理连续打卡/缺勤天数?
考勤里最常遇到的是“连续出勤X天发奖励”或“连续缺勤Y天触发预警”,这不能靠 GROUP BY 解决,得用窗口函数识别连续段。核心思路是:用 ROW_NUMBER() 和分组字段的差值构造“连续组ID”。
常见错误是直接对日期排序后用 LAG() 比较前一天——这只能看相邻两条,无法自动聚合同一段。正确做法是:
- 先按员工、日期排序生成序号:
ROW_NUMBER() OVER (PARTITION BY emp_id ORDER BY work_date) - 再把日期转成序号(如
TO_DAYS(work_date)或DATEDIFF(work_date, '2000-01-01')) - 两者相减,相同结果即属同一连续段
SELECT emp_id, work_date,
DATEDIFF(work_date, '2000-01-01') - ROW_NUMBER() OVER (PARTITION BY emp_id ORDER BY work_date) AS grp
FROM attendance
WHERE status = 'present';这个 grp 就是连续出勤段标识,后续可套一层 COUNT(*) OVER (PARTITION BY emp_id, grp) 算长度。
如何用 LEAD()/LAG() 判断班次衔接是否合规?
排班系统常要求“夜班后必须休息24小时,不能连上早班”。这时要跨行取值比对,LAG() 和 LEAD() 是唯一直接手段。
注意三点:
-
LAG(shift_type, 1) OVER (PARTITION BY emp_id ORDER BY start_time)取前一条班次,但必须确保ORDER BY字段能反映真实时间顺序(推荐用start_time,别用schedule_id) - 如果存在同一天多班次,需在
PARTITION BY中加入日期维度,否则跨天逻辑会错乱 - MySQL 8.0+ 和 PostgreSQL 支持
LAG(..., N),但 SQLite 不支持偏移量参数,只能取前1条
示例中判断“夜班后是否紧接早班”:
SELECT emp_id, start_time, shift_type,
LAG(shift_type) OVER (PARTITION BY emp_id ORDER BY start_time) AS prev_shift,
CASE WHEN shift_type = 'morning'
AND LAG(shift_type) OVER (PARTITION BY emp_id ORDER BY start_time) = 'night'
THEN 'violation' END AS rule_break
FROM schedule;
RANK() 和 ROW_NUMBER() 在排班优先级排序中怎么选?
当多个员工竞聘同一时段班次(如周末早班),需按规则排序并取 Top 1。这里容易混淆的是:
-
ROW_NUMBER()严格递增,相同积分也会分出 1/2/3,适合“只招一人”场景 -
RANK()对相同值给相同名次(如两个95分都是第1,下一个就是第3),适合“名额不限,按分档录取” -
DENSE_RANK()在中间档位更紧凑(两个95分都是第1,下一个就是第2),适合排班池滚动替补
实际排班中,若按工龄+考勤分综合打分,且允许并列,则用 RANK();若强制唯一上岗顺序(比如抽签决定并列者),就得加随机因子:ROW_NUMBER() OVER (ORDER BY score DESC, RAND())
MySQL 5.7 不支持窗口函数,有什么轻量替代方案?
很多老系统还在用 MySQL 5.7,没法用 OVER。此时只能靠变量模拟,但风险高:
- 变量执行顺序不保证,尤其在有
ORDER BY和LIMIT混用时极易出错 - 无法在子查询或视图里安全复用,必须写成单层
SELECT - 替代方案优先级:先看能否升级到 8.0;其次考虑用应用层(Python/Java)做二次计算;实在不行再用变量,且必须加
ORDER BY强制排序,并用SELECT ... INTO @var初始化
典型变量写法(仅限简单序列):
SET @rn := 0;
SELECT emp_id, work_date,
(@rn := @rn + 1) AS row_num
FROM attendance
ORDER BY emp_id, work_date;但涉及多分区(如每个员工独立编号)时,变量极易混乱,不如直接导出后用脚本处理。
窗口函数真正难的不是语法,而是把业务里的“连续”“前后关系”“动态分组”准确映射成数学上的有序序列差或偏移比较——写错一行 ORDER BY,整个逻辑就漂移了。

















