按日期透视DateTime字段:如何提取员工每日首次上班打卡记录?
获取员工每日首次上班打卡记录的SQL实现
针对你需要提取员工每日首次上班打卡(punch_type=1)记录的需求,这里提供两种实用的SQL方案,适配大多数主流关系型数据库(如MySQL、SQL Server、PostgreSQL等):
方法一:使用聚合函数MIN()(简洁高效)
如果只需要获取首次上班的打卡时间,这种方法最直接,通过过滤上班打卡记录后按员工+日期分组,取最小的打卡时间即可。
SELECT emp_num, report_date, MIN(punch_time) AS first_punch_in_time FROM punch_records WHERE punch_type = 1 -- 仅筛选上班打卡类型 GROUP BY emp_num, report_date ORDER BY emp_num, report_date;
说明:
- 先通过
WHERE punch_type=1过滤出所有上班打卡记录; - 按
emp_num(员工编号)和report_date(打卡日期)分组,用MIN(punch_time)取出每组中最早的打卡时间; - 针对你提供的示例数据,执行后会得到员工1在2018-04-20的首次上班打卡时间为
2018-04-20 04:46:00.000。
如果存在report_date和punch_time日期不一致的情况(比如跨天打卡),可以改用DATE(punch_time)作为分组依据,确保按实际打卡日期统计:
SELECT emp_num, DATE(punch_time) AS actual_punch_date, MIN(punch_time) AS first_punch_in_time FROM punch_records WHERE punch_type = 1 GROUP BY emp_num, DATE(punch_time) ORDER BY emp_num, actual_punch_date;
方法二:使用窗口函数ROW_NUMBER()(灵活扩展)
如果需要保留打卡记录的完整字段(比如后续要关联其他信息),或者处理有并列最早打卡的场景,窗口函数会更灵活。
WITH ranked_punches AS ( SELECT emp_num, report_date, punch_time, punch_type, -- 按员工+日期分组,按打卡时间升序标记行号 ROW_NUMBER() OVER ( PARTITION BY emp_num, report_date ORDER BY punch_time ASC ) AS rn FROM punch_records WHERE punch_type = 1 ) SELECT emp_num, report_date, punch_time AS first_punch_in_time, punch_type FROM ranked_punches WHERE rn = 1 -- 取每组中排名第一的记录(即首次打卡) ORDER BY emp_num, report_date;
说明:
- 先用CTE(公共表表达式)给每个员工每天的上班打卡记录按时间排序并标记行号
rn; - 筛选出行号为1的记录,就是该员工当日的首次上班打卡;
- 如果存在同一时间多次上班打卡的情况,
ROW_NUMBER()会随机返回其中一条,若要保留所有并列最早的记录,可以替换为RANK()或DENSE_RANK()。
内容的提问来源于stack exchange,提问作者jmarusiak
相关产品推荐
相关产品推荐

