【问题标题】:How to get the time spent inside and outside in SQLSQL如何获取内外花费的时间
【发布时间】:2014-03-26 20:26:39
【问题描述】:

我有一个简单的表格,用于存储员工全天的打卡和打卡:

╔══════════╦══════════════════╦════════════╗
║ Employee ║  PunchDateTime   ║ ActionType ║
╠══════════╬══════════════════╬════════════╣
║ John     ║ 2014/03/26 08:00 ║ IN         ║
║ Mark     ║ 2014/03/26 08:12 ║ IN         ║
║ John     ║ 2014/03/26 08:50 ║ OUT        ║
║ John     ║ 2014/03/26 09:29 ║ IN         ║
║ Mark     ║ 2014/03/26 10:35 ║ OUT        ║
║ John     ║ 2014/03/26 10:55 ║ OUT        ║
║ Mark     ║ 2014/03/26 11:42 ║ IN         ║
║ John     ║ 2014/03/26 12:38 ║ IN         ║
║ John     ║ 2014/03/26 16:21 ║ OUT        ║
║ Mark     ║ 2014/03/26 16:49 ║ OUT        ║
╚══════════╩══════════════════╩════════════╝

我想构建一个计算进出时间的查询。最终结果应如下所示:

╔══════════╦══════════════════╦════════════╦════════╦═════════╗
║ Employee ║  PunchDateTime   ║ ActionType ║ TimeIn ║ TimeOut ║
╠══════════╬══════════════════╬════════════╬════════╬═════════╣
║ John     ║ 2014/03/26 08:00 ║ IN         ║ -      ║ -       ║
║ Mark     ║ 2014/03/26 08:12 ║ IN         ║ -      ║ -       ║
║ John     ║ 2014/03/26 08:50 ║ OUT        ║ 00:40  ║ -       ║
║ John     ║ 2014/03/26 09:29 ║ IN         ║ -      ║ 00:39   ║
║ Mark     ║ 2014/03/26 10:35 ║ OUT        ║ 02:23  ║ -       ║
║ John     ║ 2014/03/26 10:55 ║ OUT        ║ 01:26  ║ -       ║
║ Mark     ║ 2014/03/26 11:42 ║ IN         ║ -      ║ 01:07   ║
║ John     ║ 2014/03/26 12:05 ║ IN         ║ -      ║ 01:10   ║
║ John     ║ 2014/03/26 16:21 ║ OUT        ║ 04:16  ║ -       ║
║ Mark     ║ 2014/03/26 16:49 ║ OUT        ║ 05:07  ║ -       ║
╚══════════╩══════════════════╩════════════╩════════╩═════════╝

标准:

  • 结果需要由用户按一天计算。如果间隔超过一天,则必须计算每天花费的总时间
  • 将忽略双进/出,应使用最外层(因此 in,in,out 表示从先入到出的跟踪时间)
  • 我只需要每日结果。如果员工在一天结束时进入,并在第二天退出,它将被忽略。从第一个有效 IN 和最后一个有效 OUT 开始。
  • 仅计算有效的 IN 和 OUT 对所花费的时间,排除无法配对的那些(两个或多个连续 IN/OUT)

SqlFiddle

如何计算 TimeIn 和 TimeOut 的图形表示(针对一名员工):

【问题讨论】:

  • 您需要每天的结果吗?还是按用户?从您显示的预期结果中看不清楚。
  • 我觉得从sql server获取数据然后用你的代码解析比较容易实现
  • 如果有人打卡两次但中间没有打卡怎么办?
  • 根据经验,这是一种非常复杂的系统类型,需要建模,因为您通常会开始考虑“如果他在通常 8:00 开始的情况下在 7:15 打卡怎么办。他是否完成了 45 分钟?那么他可以提前离开而不被扣除时间吗?如果他在 6:45 打卡怎么办?如果他当天第一次在 12:00 打卡怎么办,那是合法的还是应该在 9 点之前扣除 1 小时,并且有申请假期/休息时间?午休期间打卡/打卡怎么样?”
  • 只计算有效的 IN 和 OUT 对所花费的时间,排除那些无法配对的(两个或多个连续的 IN/OUT)。

标签: sql sql-server datetime


【解决方案1】:

给你:

if object_id('tempdb..#Punch') is not null
    drop table #Punch

create table #Punch(
    Employee varchar(50) not null,
    PunchDateTime datetime not null,
    ActionType varchar(3) not null
)

insert into #Punch select 'John', '2014/03/26 06:00', 'OUT' -- extra "out"
insert into #Punch select 'John', '2014/03/26 08:00', 'IN'
insert into #Punch select 'John', '2014/03/26 08:01', 'IN' -- extra "in"
insert into #Punch select 'John', '2014/03/26 08:02', 'IN' -- extra "in"
insert into #Punch select 'John', '2014/03/26 08:03', 'IN' -- extra "in"
insert into #Punch select 'Mark', '2014/03/26 08:12', 'IN'
insert into #Punch select 'John', '2014/03/26 08:50', 'OUT'
insert into #Punch select 'John', '2014/03/26 08:51', 'OUT' -- extra "out"
insert into #Punch select 'John', '2014/03/26 08:52', 'OUT' -- extra "out"
insert into #Punch select 'John', '2014/03/26 08:53', 'OUT' -- extra "out"
insert into #Punch select 'John', '2014/03/26 09:29', 'IN'
insert into #Punch select 'Mark', '2014/03/26 10:35', 'OUT'
insert into #Punch select 'John', '2014/03/26 10:55', 'OUT'
insert into #Punch select 'Mark', '2014/03/26 11:42', 'IN'
insert into #Punch select 'John', '2014/03/26 12:38', 'IN'
insert into #Punch select 'John', '2014/03/26 16:21', 'OUT'
insert into #Punch select 'Mark', '2014/03/26 16:49', 'OUT'

select *
from (
    select
        p.Employee,
        p.PunchDateTime,
        p.ActionType,
        case when p.ActionType = 'IN' 
            then '-' 
            else coalesce(substring(convert(varchar(30), p.PunchDateTime - p.PreviousPunchDateTime, 20), 12, 5), '-')
        end as TimeIn,
        case when p.ActionType = 'OUT' 
            then '-' 
            else coalesce(substring(convert(varchar(30), p.PunchDateTime - p.PreviousPunchDateTime, 20), 12, 5), '-')
        end as TimeOut
    from (
        select
            a.Employee,
            a.PunchDateTime,
            a.ActionType,
            (
                select top 1 b.PunchDateTime 
                from #Punch b 
                where b.Employee = a.Employee 
                    and b.PunchDateTime < a.PunchDateTime
                    and b.ActionType <> a.ActionType
                    and datediff(day, b.PunchDateTime, a.PunchDateTime) = 0 -- same day
                    and not exists(
                        select 1
                        from #Punch c
                        where c.Employee = a.Employee
                            and c.ActionType = a.ActionType
                            and c.PunchDateTime < a.PunchDateTime
                            and c.PunchDateTime > b.PunchDateTime
                    )
                    and not (
                        b.ActionType = 'OUT'
                        and not exists(select 1 from #Punch d where d.Employee = a.Employee and d.ActionType = 'IN' and d.PunchDateTime < b.PunchDateTime)
                    )
                order by b.PunchDateTime asc
            ) as PreviousPunchDateTime
        from #Punch a
    ) p
) p2
where not (p2.ActionType = 'OUT' and p2.TimeIn = '-' and p2.TimeOut = '-')
    and not (
        p2.ActionType = 'IN' and p2.TimeIn = '-' and p2.TimeOut = '-'
        and exists(select 1 from #Punch a where a.ActionType = 'IN' and a.Employee = p2.Employee and a.PunchDateTime < p2.PunchDateTime))
order by
    p2.PunchDateTime

【讨论】:

  • 谢谢,但是第二个条件有问题:双进/出会被忽略,应该使用最外层(所以in,in,out表示从第一个进到出的跟踪时间)
  • @milo2011- 我修改了答案以考虑其他标准
  • 双输入正确,但双输出错误。应该计算在 IN 之后找到的最后一个 OUT。
  • 你确定吗?对我来说,我们将第一次“打入”视为工人“打入”的时候是有道理的。似乎我们想将第一次“出局”视为工人“出局”的时候。
  • 请看我在上面的问题描述中添加的照片。
猜你喜欢
  • 2013-04-10
  • 2018-01-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-04
  • 1970-01-01
  • 1970-01-01
  • 2021-12-25
相关资源
最近更新 更多