我有一个表,保存从指纹机获取的员工进出时间。
员工在一段时间内(例如3分钟)可以在同一个入口和/或导出拥有多个指纹,我们称之为A。
如果员工想要确保机器获得指纹,他们会在两种情况下执行此操作。如果公司里有很多指纹机,一个靠近大门,第二个靠近他们的办公室。
员工每天可以根据要求多次进出,必须在一段时间内在公司停留过,才能算作工作时间,并确定下一次指纹是否退出.(例如20分钟)我们称之为B
问题:
在确定插入数据库的记录的进入和退出次数的过程中,有两个方面:
如果指纹进入,我们应该选择A内的最短时间 并在 B 到达后考虑下一个指纹作为退出,但选择最大时间
这是该月的所有日子。
请注意,有时工作日从上午 08:00 开始,到第二天上午 07:59 结束,我们称之为 C。
示例
emp_id edate etime
100 01/01/2015 08:00:00
100 01/01/2015 08:00:30
100 01/01/2015 08:00:58
100 01/01/2015 08:02:01
100 01/01/2015 10:00:00
100 01/01/2015 10:01:15
100 01/01/2015 10:01:50
100 01/01/2015 12:10:00
100 01/01/2015 12:10:50
100 01/01/2015 12:11:00
100 01/01/2015 13:50:10
100 01/01/2015 13:52:30
100 01/01/2015 13:52:31
100 02/01/2015 01:00:31
100 02/01/2015 01:01:31
100 02/01/2015 01:52:31
100 02/01/2015 04:59:31
我想编写一个 SQL Server 查询来显示结果:
emp_id edate InTime OutTime
100 01/01/2015 08:00:00 10:01:50
100 01/01/2015 12:10:00 13:52:31
100 01/01/2015 01:00:31 01:52:31
100 01/01/2015 01:00:31 01:52:31
100 01/01/2015 04:59:31 null
第二天也一样...
我可以在任何 UI 语言的 datagrid
中使用循环和条件来完成此操作,但这需要花费大量时间,特别是在为许多员工计算整整一个月或更长时间时。
最佳答案
如果您的 SQL 版本是 2012 或更高版本,请尝试以下查询:
select emp_id,[date],max(intime) as InTime, max(outtime) as OutTime
from
(
select
emp_id,
cast(combdt as date) as [date],
case
when row_number() over (partition by emp_id,cast(combdt as date) order by etime1 asc)%2 =1
then etime1 else null
end as intime,
case
when row_number() over (partition by emp_id,cast(combdt as date) order by etime1 asc)%2 =0
then etime2 else null
end as outtime,
(row_number() over (partition by emp_id,cast(combdt as date) order by etime1 asc)+1)/2 as badge
from
(
--since min of entry is taken and max of exit is taken
-- I'm apply the comparision between min and max to determine the logic of B
select * from
(
select
t.emp_id,
t.combdt,
min(t.combdt) as etime1,
t.etime2,
case
when DATEDIFF(mi,ISNULL(lag(etime2) over(partition by t.emp_id,cast(combdt as date) order by etime2),0),min(t.combdt)) >20
then 1
else 0
end as flag
from
(
select
t1.emp_id,
t1.combdt,
max(t2.combdt) as etime2,
max(t2.r) as r2
from
(
select
*,
edate+etime as combdt,
row_number() over(partition by emp_id, edate order by etime asc) as r
from tbl
) t1
left join
(
select
*,
edate+etime as combdt,
row_number() over(partition by emp_id, edate order by etime asc) as r
from
tbl
) t2
on t1.emp_id=t2.emp_id and
dateadd(mi,3,t1.combdt)>t2.combdt -- this is where we put A
group by t1.emp_id, t1.combdt,t1.r
)t
group by t.emp_id,t.combdt,t.etime2
)t where flag =1
)t
)t
group by emp_id,[date],badge
这个长查询的输出是:
emp_id date InTime OutTime
100 2015-01-01 2015-01-01 08:00:00.000 2015-01-01 10:01:50.000
100 2015-01-01 2015-01-01 12:10:00.000 2015-01-01 13:52:31.000
100 2015-02-01 2015-02-01 01:00:31.000 2015-02-01 01:52:31.000
100 2015-02-01 2015-02-01 04:59:31.000 NULL
演示的 SQl fiddle 链接在这里:http://sqlfiddle.com/#!6/e8762/4
P.S.:请注意,上面的问题太长了,因为它由多个小问题(例如 A 和 B、日期重叠约束以及从连续条目计算输入输出)组成,并且不提供 SQL 版本。
如果您使用的 SQL Server 版本不支持lag/lead
函数,请考虑使用JOIN
。
有许多 SO 示例将向您展示如何做到这一点。
关于sql-server - 如何在 SQL Server 中将工作日中多个时间戳的时间分类为“入”和“出”,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32196513/