如何在 MySQL 中使用 ROW_NUMBER 来检查每位员工每天的行数?
Posted
技术标签:
【中文标题】如何在 MySQL 中使用 ROW_NUMBER 来检查每位员工每天的行数?【英文标题】:How do I use ROW_NUMBER in MySQL to check number of rows per day per employee? 【发布时间】:2021-08-11 17:21:40 【问题描述】:我在一个表格中存储打卡和打卡数据,如下所示:
id | empid | punchtime |
---|---|---|
74 | 4 | 2021-08-04 17:11:54 |
171 | 4 | 2021-08-06 13:47:45 |
202 | 4 | 2021-08-09 10:14:01 |
271 | 4 | 2021-08-09 18:20:01 |
308 | 4 | 2021-08-10 11:14:54 |
343 | 4 | 2021-08-10 14:46:21 |
349 | 4 | 2021-08-10 15:22:10 |
380 | 4 | 2021-08-10 18:10:58 |
406 | 4 | 2021-08-11 10:13:48 |
我想检查每天的打卡和打卡。因此,每个奇数的第 n 拳被认为是一个拳入,每个偶数的第 n 个拳被认为是一个拳出。我还有一个包含员工信息的表格,即员工 ID 和姓名。
这是我的 SQL 查询:
WITH PunchDataPlus AS (
SELECT pd.*, ei.Name, ROW_NUMBER() OVER(PARTITION BY EmpID, datediff(PunchTime, '2021-08-11')=0 ORDER BY PunchTime) AS RN
FROM PunchData pd LEFT JOIN EmployeeInfo ei ON pd.EmpID=ei.EmpID
), FinalPunchData AS (
SELECT PunchDataPlus.*, CASE WHEN RN % 2 =1 THEN 'IN' ELSE 'OUT' END AS inOutCol FROM PunchDataPlus
)
SELECT * FROM FinalPunchData WHERE EmpID=4 ORDER BY PunchTime DESC;
这个查询的问题是datediff
一次只能比较一天。如何与每一天进行比较,以便每一天都有自己的输入/输出值?
SQL查询的结果:
id | EmpID | PunchTime | Name | RN | inOutCol |
---|---|---|---|---|---|
406 | 4 | 2021-08-11 10:13:48 | redacted | 1 | IN |
380 | 4 | 2021-08-10 18:10:58 | redacted | 8 | OUT |
349 | 4 | 2021-08-10 15:22:10 | redacted | 7 | IN |
343 | 4 | 2021-08-10 14:46:21 | redacted | 6 | OUT |
308 | 4 | 2021-08-10 11:14:54 | redacted | 5 | IN |
271 | 4 | 2021-08-09 18:20:01 | redacted | 4 | OUT |
202 | 4 | 2021-08-09 10:14:01 | redacted | 3 | IN |
171 | 4 | 2021-08-06 13:47:45 | redacted | 2 | OUT |
74 | 4 | 2021-08-04 17:11:54 | redacted | 1 | IN |
我正在寻找的结果:
id | EmpID | PunchTime | Name | RN | inOutCol |
---|---|---|---|---|---|
406 | 4 | 2021-08-11 10:13:48 | redacted | 1 | IN |
380 | 4 | 2021-08-10 18:10:58 | redacted | 4 | OUT |
349 | 4 | 2021-08-10 15:22:10 | redacted | 3 | IN |
343 | 4 | 2021-08-10 14:46:21 | redacted | 2 | OUT |
308 | 4 | 2021-08-10 11:14:54 | redacted | 1 | IN |
271 | 4 | 2021-08-09 18:20:01 | redacted | 2 | OUT |
202 | 4 | 2021-08-09 10:14:01 | redacted | 1 | IN |
171 | 4 | 2021-08-06 13:47:45 | redacted | 1 | IN |
74 | 4 | 2021-08-04 17:11:54 | redacted | 1 | IN |
【问题讨论】:
所以你总是想假设他们在午夜出去(无论打卡时间在哪个时区,我真的希望是UTC)? @ysth 是的,没错。 【参考方案1】:您可以尝试将datediff(PunchTime, '2021-08-11')=0
更改为DATE(PunchTime)
。这将仅提取您的日期时间值的日期部分,从而根据需要按每天对您的数据进行分区。
让我知道这是否适合你。
【讨论】:
这正是我想要的。谢谢!【参考方案2】:你不需要比较天,你只需要你的窗口是empid和天:
select pd.*,
case (row_number() over (partition by empid, date(punchtime) order by punchtime)) % 2
when 1 then 'IN'
else 'OUT'
end in_out
from PunchData pd
fiddle
但你真的不应该这样做。您应该将 IN/OUT 与打孔数据一起存储;当有人进出时,他们知道自己的意图,并且应该存储该意图。即使由于某种原因您只能获得原始时间,您也应该在导入数据时计算 IN/OUT 并将其存储,以便推断意图时的错误可以在数据中得到纠正,而不是持久化。
这种方法的其他问题:您不支持在午夜之前和之后打卡。如果您以 UTC 格式存储时间(您应该这样做),则 UTC 午夜可能不是您划定所有人都外出的界限的不合适时间。如果您没有以 UTC 格式存储时间,则会遇到夏令时问题,即在凌晨 1:45 打卡(以美国 DST 规则为例)之后可能会在凌晨 1:15 打卡,出现乱序(以及使时间计算不明确)。
【讨论】:
对我的回答添加了一些评论【参考方案3】:请检查这个。使用特定 empid 搜索时启用 where 子句,否则禁用它。
-- mysql(v5.8)
SELECT t.id
, t.empid
, t.punchtime
, ei.name
, t.row_num RN
, CASE t.row_num % 2
WHEN 1 THEN 'IN'
ELSE 'OUT'
END inOutCol
FROM (SELECT id
, empid
, punchtime
, ROW_NUMBER() OVER (PARTITION BY empid, CAST(punchtime AS DATE) ORDER BY punchtime) row_num
FROM PunchData
WHERE empid = 4) t
LEFT JOIN EmployeeInfo ei
ON t.empid = ei.empid
ORDER BY t.punchtime DESC;
请查看网址https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=4236c0424e87fe25be6033dcc93b4856
【讨论】:
这里不需要子查询。不确定 5.7 的评论是干什么用的;这在 5.7 中不起作用。 嗨@ysth 尝试写这个以提高可读性并考虑性能。这里提到版本是因为你知道低版本的 mysql 不支持 ROW_NUMBER()。 嗨@AidanHakimian,您可以在 PunchData 表中添加标志列,以区分打卡和打卡。即 1 = IN , 2 = OUT【参考方案4】:您没有提供员工表,因此我将其从查询中删除。
请将其添加到您的数据库中
CREATE TABLE PunchData (`id` int, `empid` int, `punchtime` varchar(19)) ; INSERT INTO PunchData (`id`, `empid`, `punchtime`) VALUES (74, 4, '2021-08-04 17:11:54'), (171, 4, '2021-08-06 13:47:45'), (202, 4, '2021-08-09 10:14:01'), (271, 4, '2021-08-09 18:20:01'), (308, 4, '2021-08-10 11:14:54'), (343, 4, '2021-08-10 14:46:21'), (349, 4, '2021-08-10 15:22:10'), (380, 4, '2021-08-10 18:10:58'), (406, 4, '2021-08-11 10:13:48') ;
WITH PunchDataPlus AS ( SELECT pd.* , ROW_NUMBER() OVER(PARTITION BY EmpID, DATE(PunchTime) ORDER BY PunchTime) AS RN FROM PunchData pd ), FinalPunchData AS ( SELECT PunchDataPlus.*, CASE WHEN RN % 2 =1 THEN 'IN' ELSE 'OUT' END AS inOutCol FROM PunchDataPlus ) SELECT * FROM FinalPunchData WHERE EmpID=4 ORDER BY PunchTime DESC;
编号 |空 |打卡时间 |注册护士 | inOutCol --: | ----: | :----------------- | -: | :-------- 406 | 4 | 2021-08-11 10:13:48 | 1 |在 380 | 4 | 2021-08-10 18:10:58 | 4 |出去 349 | 4 | 2021-08-10 15:22:10 | 3 |在 第343章4 | 2021-08-10 14:46:21 | 2 |出去 308 | 4 | 2021-08-10 11:14:54 | 1 |在 271 | 4 | 2021-08-09 18:20:01 | 2 |出去 202 | 4 | 2021-08-09 10:14:01 | 1 |在 171 | 4 | 2021-08-06 13:47:45 | 1 |在 74 | 4 | 2021-08-04 17:11:54 | 1 |在
db小提琴here
【讨论】:
以上是关于如何在 MySQL 中使用 ROW_NUMBER 来检查每位员工每天的行数?的主要内容,如果未能解决你的问题,请参考以下文章
MySQL 中用于插入的 ROW_NUMBER() 等效项[重复]
如何在 MySQL 中对 GROUP BY 结果的 SELECT INTO 使用自动增量?