【问题标题】:How to get the first employee entrance in a day near his shift如何在接近轮班的一天内获得第一个员工入口
【发布时间】:2015-05-21 19:17:07
【问题描述】:

我有一个 employee_attendance 表 (SQL Server 2012),其中包含以下列:

  • AttendanceId(身份)
  • 员工编号
  • 时间戳(打卡或打卡)
  • 访问代码(I = IN,O = OUT)

这是带有一些示例数据的 SQL Fiddlehttp://sqlfiddle.com/#!3/ba8a1/1

我知道这些员工可以在我们拥有的 3 个轮班中的任何一天工作:

  1. 班次 A(从 07:00 到 15:00)
  2. 班次 B(从 15:00 到 23:00)
  3. C 班(从 23:00 到次日 07:00)

所以我需要知道每个员工在选定的一天(样本数据只有一天,并且已过滤为仅显示 IN 记录,因此仅显示 IN 访问公司)是第一个靠近(下方或上方)Shift 入口的Timestamp

这是应用正确脚本后 SQL Fiddle 数据应该是什么样子的图像:

【问题讨论】:

  • 对 sql 摆弄示例数据的做法赞不绝口!
  • 对于employee id 116,您指示正确的时间戳是轮班开始前45分钟,而另一个时间戳(15:25:43)则只有25分钟。所以你不是在寻找最近的,对吗?如果 14:15:35 实际上是 12:15:35 怎么办?员工真的会早到还是迟到 25 分钟?数据是否足够干净,可以说在给定的一天中每个员工总是只有一个班次,如果存在,我们总是可以使用早期时间戳,如果不使用晚期时间戳?
  • 你怎么知道某人正在轮班?
  • 请参阅"Should questions include “tags” in their titles?",其中的共识是“不,他们不应该”!

标签: sql sql-server sql-server-2008


【解决方案1】:

由于您拥有 SQL Server 2012,您可以使用 TimeFromParts 和“mod 8”一切来最大程度地减少工作...

select employeeId,
       accessCode,
       minDiff = MIN(ABS(DATEDIFF(TIMEFROMPARTS(DATEPART(HH,t.timestamp) % 8,
                                               (DATEPART(MI,t.timestamp),
                                               (DATEPART(S,t.timestamp),0,0),
                                  TIMEFROMPARTS(7,
                                               (DATEPART(MI,t.timestamp),
                                               (DATEPART(S,t.timestamp))
from table t
were t.timestamp is in a given daily range

因此,由于您的班次更改 (7,15,23) 都是“mod 8 = 7”,我只需将时间戳“mod 8”的小时部分与“7”进行比较——这样我就不需要运行查询 3 次。

【讨论】:

  • 我喜欢 mod 8 的优雅,下次遇到类似问题时我必须记住这一点。
【解决方案2】:

SQLFiddle(感谢提供示例数据):http://sqlfiddle.com/#!3/ba8a1/7/0

查询:

declare @day datetime = '5-20-15' --not necessary, but included for more flexibility; you can just hardcode the date below if you want

SELECT *, '7:00:00 shift' as Shift
, abs(datediff(minute, timestamp, @day + '7:00:00')) as TimeDiff
into #temp
FROM employee_attendance
union all
SELECT *, '15:00:00 shift'
, abs(datediff(minute, timestamp, @day + '15:00:00'))
FROM employee_attendance
union all
SELECT *, '23:00:00 shift'
, abs(datediff(minute, timestamp, @day + '23:00:00'))
FROM employee_attendance

select * from (
  select *
  , row_number() over (partition by EmployeeID order by TimeDiff) as RowN
  from #temp
  ) a
where RowN = 1

解释:

  1. 为每个员工,每个班次开始时间创建一行。
  2. 计算实际开始时间与所列打卡时间之间的差异。取绝对值,因为原始请求正在寻找最近的打卡,不一定在轮班开始之前。
  3. 使用 row_number 来标识每个员工与轮班开始时间相差最短的行。
  4. 只返回最接近班次开始时间的行。

正如其他人所指出的,这假设员工每天只有一个班次,并且最接近班次时间的打卡是有效的。对于每天一个班次的系统,提出替代标准可能更有意义 - 例如,查看绝对最早打卡或给定窗口内的最早打卡(比如 6:30 到 7: 30,对于 7:00 的班次),但这不是此请求者所要求的。

【讨论】:

  • 感谢@TabAlleman 的反馈 - 我添加了更多详细信息。
猜你喜欢
  • 1970-01-01
  • 2021-12-19
  • 2011-11-17
  • 1970-01-01
  • 2012-07-10
  • 2012-12-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多