如何通过查看过去的签到时间来区分白班和夜班?

How to distinguish between day and night shifts by looking at past sign in times?

目前,报告使用预定义的时间。但有时员工会超时工作,他们会偷偷进入下一个班次报告。如何修改报告查询以查看过去的登录时间以避免这种情况?在TIMEATT 列中,1 是入口,2 是出口。浅蓝色突出显示正确的条目,黄色表示错误的条目。

29 日的白班报告有“完成,简的”夜班入职时间

29 号的夜班报告有白班的“Do,Jone's”进入时间

下面的查询创建了一个临时文件 table,其中的数据与屏幕截图中的数据相同。

CREATE TABLE #temptable ( [Company] nvarchar(60), [ID] int, [NAME] nvarchar(130), [TIMEATT] int, [Time_CST] datetime )
INSERT INTO #temptable
values
( N'Company Ltd.', 11111, N'Done, Jane', 1, N'2019-04-28T18:00:27' ), 
( N'Company Ltd.', 11111, N'Done, Jane', 1, N'2019-04-28T18:00:28' ), --Sometimes people tap their card twice 
( N'Company Ltd.', 11111, N'Done, Jane', 2, N'2019-04-29T06:00:55' ),  
( N'Company Ltd.', 11111, N'Done, Jane', 1, N'2019-04-29T06:01:55' ),  
( N'Company Ltd.', 11111, N'Done, Jane', 2, N'2019-04-29T06:04:55' ),  
( N'Company Ltd.', 11111, N'Done, Jane', 1, N'2019-04-29T18:00:27' ), 
( N'Company Ltd.', 11111, N'Done, Jane', 2, N'2019-04-30T06:13:55' ),  
( N'Company Ltd.', 22222, N'Do, Jone', 1, N'2019-04-29T06:20:17' ), 
( N'Company Ltd.', 22222, N'Do, Jone', 2, N'2019-04-29T06:47:12' ), 
( N'Company Ltd.', 22222, N'Do, Jone', 1, N'2019-04-29T10:33:33' ), 
( N'Company Ltd.', 22222, N'Do, Jone', 2, N'2019-04-29T18:05:33' ),
( N'Company Ltd.', 22222, N'Do, Jone', 1, N'2019-04-29T18:06:33' ),
( N'Company Ltd.', 22222, N'Do, Jone', 2, N'2019-04-29T18:09:33' ),
( N'Company Ltd.', 22222, N'Do, Jone', 1, N'2019-04-30T06:05:33' ),
( N'Company Ltd.', 22222, N'Do, Jone', 2, N'2019-04-30T16:05:33' )
-- Test table
-- 1 is entry and 2 is exit. Sometimes people double swipe their cards which results in two 1 entries.
select * from #temptable as T

--Report start
set deadlock_priority low;
declare @shift varchar(10) = 'night';  --Option to switch between day and night
declare @reportdate datetime = '2019-04-29'; --Date to be ran
declare @starttime datetime;
declare @endtime datetime;

select @starttime = (case
                         when @shift = 'day' then
                             convert(datetime, @reportdate) + cast('04:00:00.000' as datetime)
                         when @shift = 'night' then
                             convert(datetime, @reportdate) + cast('16:00:00.000' as datetime)
                     end
                    );
select @endtime = (case
                       when @shift = 'day' then
                           convert(datetime, @reportdate) + cast('23:59:59.000' as datetime)
                       when @shift = 'night' then
                           convert(datetime, dateadd(d, 1, @reportdate)) + cast('11:59:59.000' as datetime)
                   end
                  );


select Company
     , NAME
     , EmpID
     , startTime
     , endTime
     , sum(datediff(second, startTime, endTime) / 3600.0) as HrsWorked
from
( -- sub query to get matching exit time for each entry if it exists
    select Company
         , NAME
         , ID                                                                 as EmpID
         , Time_CST                                                           as startTime
         , lead(Time_CST, 1, null) over (partition by NAME order by Time_CST) as endTime
         , TIMEATT
         , Time_CST
    from
    ( -- subquery to exclude duplicate records
        select *
        from
        (
            select *
            from
            ( -- subquery to identify records to ignore
                select Company
                     , NAME
                     , ID
                     , TIMEATT
                     , Time_CST
                     , case lead(TIMEATT, 1, 0) over (partition by NAME order by Time_CST)
                           when TIMEATT then
                               1
                           else
                               0
                       end as Exclude                                    
                from  #temptable 

            ) a
            where Exclude = 0

        ) t
    ) n
) z
where TIMEATT = 1 -- filter so left column is always entry time.
      and startTime >= @starttime
      and endTime <= @endtime
--and Company in (@contractornames)
group by z.Company
       , z.NAME
       , z.EmpID
       , z.startTime
       , z.endTime
order by Company
       , NAME
       , startTime


--DROP TABLE #temptable

首先,我将您的查询更改为使用 CTE 而不是 3 级子查询。使其更易于阅读。

为了识别错误的打孔,我在您的查询中扩展了 CASE 语句,该语句识别重复的打孔并将它们标记为排除。使用 LEAD 功能,我检查了下一次冲压是否在 10 分钟内。如果是,则将其标记为排除。

显然,此解决方案并不完美,但可以让您到达需要的地方,并对错误有合理的容忍度。津贴的window可以扩大或缩小。

更新:您提供的新数据集超过了 10 分钟的限额。我把它提高到 20。同样,这里有一个错误容忍度。

我还调整了 InOut 查询以不使用任何以 TIMEATT = 1 作为开始时间的查询。这确保了 outpunches 永远不会作为开始时间进入数据集。

最后,我在输出查询中又添加了一个条件,以确保开始时间与请求的报告日期一致。

IF OBJECT_ID('tempdb.dbo.#temptable', 'U') IS NOT NULL
    DROP TABLE #temptable;

CREATE TABLE #temptable ( [Company] nvarchar(60), [ID] int, [NAME] nvarchar(130), [TIMEATT] int, [Time_CST] datetime )
INSERT INTO #temptable VALUES
( N'Company Ltd.', 11111, N'Done, Jane', 1, N'2019-04-28T18:00:27' ), 
( N'Company Ltd.', 11111, N'Done, Jane', 1, N'2019-04-28T18:00:28' ), --Sometimes people tap their card twice 
( N'Company Ltd.', 11111, N'Done, Jane', 2, N'2019-04-29T06:00:55' ),  
( N'Company Ltd.', 11111, N'Done, Jane', 1, N'2019-04-29T06:01:55' ),  
( N'Company Ltd.', 11111, N'Done, Jane', 2, N'2019-04-29T06:04:55' ),  
( N'Company Ltd.', 11111, N'Done, Jane', 1, N'2019-04-29T18:00:27' ), 
( N'Company Ltd.', 11111, N'Done, Jane', 2, N'2019-04-30T06:13:55' ),  
( N'Company Ltd.', 22222, N'Do, Jone', 1, N'2019-04-29T06:20:17' ), 
( N'Company Ltd.', 22222, N'Do, Jone', 2, N'2019-04-29T06:47:12' ), 
( N'Company Ltd.', 22222, N'Do, Jone', 1, N'2019-04-29T10:33:33' ), 
( N'Company Ltd.', 22222, N'Do, Jone', 2, N'2019-04-29T18:05:33' ),
( N'Company Ltd.', 22222, N'Do, Jone', 1, N'2019-04-29T18:06:33' ),
( N'Company Ltd.', 22222, N'Do, Jone', 2, N'2019-04-29T18:09:33' ),
( N'Company Ltd.', 22222, N'Do, Jone', 1, N'2019-04-30T06:05:33' ),
( N'Company Ltd.', 22222, N'Do, Jone', 2, N'2019-04-30T16:05:33' )
-- Test table
-- 1 is entry and 2 is exit. Sometimes people double swipe their cards which results in two 1 entries.
select * from #temptable as T

--Report start
set deadlock_priority low;
declare @shift varchar(10) = 'night';  --Option to switch between day and night
declare @reportdate datetime = '2019-04-29'; --Date to be ran
declare @starttime datetime;
declare @endtime datetime;

select @starttime = (case
                         when @shift = 'day' then
                             convert(datetime, @reportdate) + cast('04:00:00.000' as datetime)
                         when @shift = 'night' then
                             convert(datetime, @reportdate) + cast('16:00:00.000' as datetime)
                     end
                    );
select @endtime = (case
                       when @shift = 'day' then
                           convert(datetime, @reportdate) + cast('23:59:59.000' as datetime)
                       when @shift = 'night' then
                           convert(datetime, dateadd(d, 1, @reportdate)) + cast('11:59:59.000' as datetime)
                   end
                  );


WITH NoDoubles AS
    (
    SELECT
        Company
        , [NAME]
        , ID
        , TIMEATT
        , Time_CST
        , CASE
            WHEN LEAD(TIMEATT, 1, 0) OVER (PARTITION BY NAME ORDER BY Time_CST) = TIMEATT THEN 1
            /* Allow for 10 minute grace period for swipes to be excluded */
            WHEN LEAD(Time_CST, 1, 0) OVER (PARTITION BY NAME ORDER BY Time_CST) = '1900-01-01 00:00:00.000' THEN 0
            WHEN LEAD(Time_CST, 1, 0) OVER (PARTITION BY NAME ORDER BY Time_CST) <= DATEADD(MINUTE, 10, Time_CST) THEN 1
            ELSE 0
            END
            AS Exclude
    FROM
        #temptable
    )
     , InOut AS
    (
    SELECT
    Company
    , [NAME]
    , ID                                                                 AS EmpID
    , IIF(TIMEATT = 1, Time_CST, NULL)                                   AS startTime
    , LEAD(Time_CST, 1, NULL) OVER (PARTITION BY NAME ORDER BY Time_CST) AS endTime
    , TIMEATT
    , Time_CST
FROM
    NoDoubles
WHERE
    Exclude = 0
    )

SELECT
    Company
    , [NAME]
    , EmpID
    , startTime
    , endTime
    , SUM(DATEDIFF(SECOND, startTime, endTime) / 3600.0) AS HrsWorked
FROM
    InOut
WHERE
    CAST(startTime AS DATE) = @reportdate
    AND startTime >= @starttime
    AND endTime <= @endtime
GROUP BY
    Company
    , [NAME]
    , EmpID
    , startTime
    , endTime
ORDER BY
    Company
    , [NAME]
    , startTime;

DROP TABLE #temptable

所以 Chris 的回答给了我一个很好的工作格式。我正在查看是否有人在凌晨 4 点到中午 12 点之间进场,而在中午 12 点到晚上 7 点之间出场将被视为白班,其他人将被视为夜班。

  set deadlock_priority low;
    declare @shift varchar(10) = 'day' --Option to switch between day and night
    declare @reportdate datetime = '2019-04-29' --Date to be ran
    declare @starttime datetime
    declare @endtime datetime



select @starttime = (case
                         when @shift = 'day' then
                             convert(datetime, @reportdate) + cast('04:00:00.000' as datetime)
                         when @shift = 'night' then
                             convert(datetime, @reportdate) + cast('16:00:00.000' as datetime)
                     end
                    )
select @endtime = (case
                       when @shift = 'day' then
                           convert(datetime, @reportdate) + cast('23:59:59.000' as datetime)
                       when @shift = 'night' then
                           convert(datetime, dateadd(d, 1, @reportdate)) + cast('11:59:59.000' as datetime)
                   end
                  )


;with NoDoubles
as (select Company
         , NAME
         , ID
         , TIMEATT
         , Time_CST
         , case
               when lead(TIMEATT, 1, 0) over (partition by NAME order by Time_CST) = TIMEATT then
                   1
               /* Allow for 10 minute grace period for swipes to be excluded */
               when lead(Time_CST, 1, 0) over (partition by NAME order by Time_CST) = '1900-01-01 00:00:00.000' then
                   0
               when lead(Time_CST, 1, 0) over (partition by NAME order by Time_CST) <= dateadd(minute, 1, Time_CST) then
                   1
               else
                   0
           end as Exclude
    from temptable)
   , DayShiftEmpIds
as (select distinct
           (case
                when count(entertimes.ID) > 0 then
                    entertimes.ID
            end
           )     as id
         , 'day' as shift
    --,case when count(entertimes.id)>0 then 'day' else 'night' end as [shift]
    from temptable entertimes
       , temptable exittimes
    where entertimes.ID = exittimes.ID
          and ((
                   (
                       entertimes.Time_CST >= @reportdate + cast('04:00:00' as datetime)
                       and entertimes.Time_CST <= @reportdate + cast('11:59:00' as datetime)
                       and entertimes.TIMEATT = 1
                   )
                   and
                   (
                       exittimes.Time_CST >= @reportdate + cast('12:00:00' as datetime)
                       and exittimes.Time_CST <= @reportdate + cast('19:59:00' as datetime)
                       and exittimes.TIMEATT = 2
                   )
               )
              )
    group by entertimes.ID)
   , NightShiftEmpIds
as (select distinct
           ID
         , 'night' as shift
    from temptable as VCCIOT
    where not exists
    (
        select id from DayShiftEmpIds where DayShiftEmpIds.ID = VCCIOT.ID
    ))
   , AllEmpIdsWithShift
as (select *
    from
    (
        select id
             , shift
        from DayShiftEmpIds
        union
        select ID
             , shift
        from NightShiftEmpIds
    ) as alldata
    where alldata.shift = @shift)
   , InOut
as (select Company
         , NAME
         , NoDoubles.ID                                                       as ID
         , Time_CST                                                           as startTime
         , lead(Time_CST, 1, null) over (partition by NAME order by Time_CST) as endTime
         , TIMEATT
         , Time_CST
    from NoDoubles
        inner join AllEmpIdsWithShift
            on AllEmpIdsWithShift.id = NoDoubles.ID
    where Exclude = 0)
select Company
     , NAME
     , ID
     , startTime
     , endTime
     , sum(datediff(second, startTime, endTime) / 3600.0) as HrsWorked
from InOut
where TIMEATT = 1 -- filter so left column is always entry time.
      and startTime >= @starttime
      and endTime <= @endtime
group by Company
       , NAME
       , ID
       , startTime
       , endTime
order by Company
       , NAME
       , startTime;