如何在 SQL 服务器中将工作日中多个时间戳的时间分类为 In 和 Out

How to classify times as In and Out for multiple timestamps in a work day in SQL Server

我有一个table可以保存从指纹机上获取的员工进出时间。

员工可以在一段时间内(例如3分钟)在同一个入口and/or出口拥有多个指纹,我们称之为A。

如果员工想确保机器获得他们的指纹,他们会在两种情况下这样做。如果公司有很多指纹机,一个靠近正门,第二个靠近办公室。

员工可以退出,并且每天可以return多次,因为他们可能会被要求这样做,在一段时间内必须留在公司才能计算为工作时间并确定下一个指纹将退出。(例如 20 分钟)我们称之为 B

问题:

在确定插入数据库的记录的进出时间的过程中有两个方面:

如果指纹进入我们应该selectA内的最短时间 在 B 达到后考虑下一个指纹作为退出,但 select 最大时间

这是一个月中的所有日子。

请注意,有时工作日从 08:00am 开始,到第二天 07:59am 结束,我们称其为 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 服务器查询来得到如下所示的结果:

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 link 演示在这里:http://sqlfiddle.com/#!6/e8762/4

P.S.: 注意上面的问题太长了,因为它由多个小问题组成,比如A和B,日期重叠约束和计算in-out来自连续条目,不提供 SQL 版本。

如果您使用的 SQL 服务器版本不支持 lag/lead 功能,请考虑使用 JOIN。 有许多 SO 示例将向您展示如何操作。

首先,我找到了一种查询员工进出的方法,通过将table次与自身连接起来,然后选择最适合的行作为一个最佳入口'min()'和第二个是 exit max(),但后来我意识到,如果我可以使用另一种更好的方法,我会 post 它作为一个答案,我希望任何人都可以采用或使用这个想法。

我注意到我时间的每一组行都有一些共同点,如果它们在同一小时内,但分钟数的差异小于(3 分钟)。

我需要从第 1 行开始的奇数行的 (max()) 作为我的第一个输入。

并且偶数行的 (min()) 是每个先前奇数行的出口。

我 select 编辑了时间列并添加了另一个计算列以确定哪个时间比另一个时间多(20 分钟)的分钟数是员工可以留在办公室的最短时间。

datepart(hour,histIn.etime) + 
(
  case when datepart(MINUTE,histIn.etime)>=20 
  then 1 else 0 end
) hr

DENSE_RANK() 的帮助下,按照我的新计算列的顺序对行进行编号。 然后我需要 select 每个奇数行作为 InTime,每个偶数行都是我的 OutTime。

,case when RowIdx%2<>0 then min(InTime) end InTime
 ,case when (RowIdx+1)%2<>0 then max(InTime) end OutTime

我在这里添加了另一个计算列,将每对这对夫妇组合在一起。

 (row_number() over (partition by emp_id,edate order by RowIdx asc)+1)/2 as badge

这是@DhruvJoshi 的手。

然后最后 select 对夫妇让他们结婚。

select emp_id,edate,max(InTime)InTime,max(OutTime)OutTime,badge Counts

插入内容如下:

create table tbl (emp_id int, edate datetime, etime time(0));

insert into tbl values
(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','10:10: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,'01/01/2015','15: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'),
(100,'02/01/2015','08:59:31'),
(100,'02/01/2015','10:59:31'),
(200,'01/01/2015','08:10:00'),
(200,'01/01/2015','08:10:30'),
(200,'01/01/2015','08:10:58'),
(200,'01/01/2015','08:12:01'),
(200,'01/01/2015','10:05:00'),
(200,'01/01/2015','10:05:15'),
(200,'01/01/2015','10:05:50'),
(200,'01/01/2015','10:15:50'),
(200,'01/01/2015','12:15:00'),
(200,'01/01/2015','12:10:50'),
(200,'01/01/2015','12:11:00'),
(200,'01/01/2015','14:50:10'),
(200,'01/01/2015','14:52:30'),
(200,'01/01/2015','14:52:31'),
(200,'02/01/2015','04:00:31'),
(200,'02/01/2015','01:01:31'),
(200,'02/01/2015','01:52:31'),
(200,'02/01/2015','04:59:31'),
(200,'02/01/2015','08:59:31'),
(200,'02/01/2015','10:59:31')

这里是 select:

select emp_id,edate,max(InTime)InTime,max(OutTime)OutTime,badge Counts
from (
       select emp_id,edate
       ,case when RowIdx%2<>0 then min(InTime) end InTime
       ,case when (RowIdx+1)%2<>0 then max(InTime) end OutTime
       ,RowIdx
       ,(row_number() over (partition by emp_id,edate order by RowIdx asc)+1)/2 as badge
       from
       (
          select emp_id,edate,etime InTime
          ,datepart(hour,histIn.etime)+(case when datepart(MINUTE,histIn.etime)>=20 then 1 else 0 end)hr
          ,DENSE_RANK() over(partition by HistIn.emp_id,HistIn.edate
          order by emp_id,edate
          ,datepart(hour,histIn.etime)+(case when datepart(MINUTE,histIn.etime)>=20 then 1 else 0 end)
          )RowIdx 
          from tbl HistIn 
       )aa
       group by emp_id,edate,RowIdx
    )bb
group by emp_id,edate,badge
order by emp_id,edate,badge

他们都在这里link: http://sqlfiddle.com/#!6/b0aa3/1

谢谢大家。