sql-server - 如何在 SQL Server 中将工作日中多个时间戳的时间分类为“入”和“出”

标签 sql-server time sql-server-2008-r2

我有一个表,保存从指纹机获取的员工进出时间。

员工在一段时间内(例如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/

相关文章:

sql-server - 计算列与当前日期时间?

c - 为什么 C struct tm (time.h) 返回错误的月份?

sql - 如何在 T-SQL 中对多个条件求和?

c# - 如何设计两组数据交集的sql查询

sql-server - 无法在 Visual Studio 2010 中创建表,出现 'The specified module could not be found (exception from Hresult: 0x800700E)' 错误

c# - 如何更改 DateTime 中的时间?

Python 仅减去一次 (HH :MM:SS) from a DateTime Column

sql - sql中如何连接两个不相关的表

SQL 服务器 2008 R2 : Show the dates day into comma separated column

sql-server - 将多个客户端数据加载到 Hadoop 的最佳实践