如何在SQL内部和外部花费时间

时间:2014-03-26 09:38:53

标签: sql sql-server datetime

我有一个简单的桌子,可以全天存储员工的时钟输入和时钟输出:

╔══════════╦══════════════════╦════════════╗
║ 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和最后一个有效的OUT开始。
  • 花费的时间仅针对有效的IN和OUT对计算,消除那些无法配对的时间(两个或多个连续的IN / OUT)

SqlFiddle

如何计算TimeIn和TimeOut(对于一名员工)的图形表示: enter image description here

1 个答案:

答案 0 :(得分:3)

你去:

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