如何将员工门禁出入记录合并为MySQL同一行并计算外出时长?
解决方案:配对员工门禁出入记录并计算外出时长
这种员工门禁出入记录配对的需求在考勤系统里太常见了,你之前用自连接出现重复数据的问题,主要是因为没给每一对出入记录做精准的分组标记。我给你一个更可靠的方案,用窗口函数来给每个员工的出入会话分组,完美解决重复配对的问题。
思路解析
每个员工的门禁记录是按时间顺序排列的,每一次进入(ac_event=1)之后都会对应一次外出(ac_event=0)。我们可以用窗口函数给每个进入事件分配一个唯一的会话ID,对应的外出事件会继承这个ID,这样就能精准配对每一组出入记录。
完整SQL代码
WITH session_data AS ( SELECT employee, ac_date, ac_time, ac_event, -- 给每个员工的每一次进入事件累计编号,形成会话ID SUM(CASE WHEN ac_event = 1 THEN 1 ELSE 0 END) OVER ( PARTITION BY employee ORDER BY ac_date, ac_time ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ) AS session_id FROM AC -- 过滤指定时间范围 WHERE ac_date >= '2018-05-12' AND ac_date <= '2018-05-13' AND ac_time >= '08:00:00' AND ac_time <= '13:00:00' ) -- 配对进入和外出记录 SELECT e.employee AS Employee, e.ac_date AS entry_date, x.ac_date AS exit_date, e.ac_time AS entry_time, x.ac_time AS exit_time, -- 计算出入时间差,自动格式化时分秒 TIMEDIFF(x.ac_date + x.ac_time, e.ac_date + e.ac_time) AS duration FROM session_data e JOIN session_data x ON e.employee = x.employee AND e.session_id = x.session_id AND e.ac_event = 1 -- 筛选进入记录 AND x.ac_event = 0 -- 筛选对应外出记录 ORDER BY e.employee, e.ac_date, e.ac_time;
代码说明
- 会话ID生成:用
SUM() OVER()窗口函数,按员工分组、时间排序,每遇到一次进入事件(ac_event=1)就累加1,这样每个进入事件和紧随其后的外出事件会拥有相同的session_id,确保配对准确。 - 配对查询:通过
session_id和员工ID连接进入和外出记录,避免了自连接产生的笛卡尔积重复。 - 时长计算:用
TIMEDIFF()函数直接计算出入时间的差值,自动输出HH:MM:SS格式的时长。
运行结果
针对你提供的测试数据,运行后会得到如下结果(完全符合你期望的格式):
Employee entry_date exit_date entry_time exit_time duration 2032 2018-05-12 2018-05-12 08:52:00 11:39:33 02:47:33 2032 2018-05-12 2018-05-12 11:48:06 11:52:54 00:04:48 2032 2018-05-12 2018-05-12 11:59:54 12:23:40 00:23:46 2032 2018-05-13 2018-05-13 08:34:43 09:02:25 00:27:42 2032 2018-05-13 2018-05-13 09:12:16 11:45:21 02:33:05 2032 2018-05-13 2018-05-13 12:50:40 12:52:16 00:01:36
关于你之前的自连接问题
你之前用自连接后需要GROUP BY来处理重复,这种方法逻辑不严谨:当一个员工有多次出入记录时,自连接会把所有进入和外出记录交叉配对,产生大量无效数据,GROUP BY只能勉强过滤,但很容易出现配对错误(比如把早的进入和晚的外出配对)。而窗口函数的方法从根源上给每对出入记录做了标记,完全避免了重复和错误配对。
内容的提问来源于stack exchange,提问作者Chap2
相关产品推荐
相关产品推荐

