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

安稳与你 提交于 2019-12-07 18:04:43

问题


Currently, the report uses predefined times. But sometimes employees overstay their shift and they creep into the next shift report. How do I modify the report query to look at the past sign in times to avoid this? In the TIMEATT column 1 is entry and 2 is exit. Light blue highlights the correct entries and yellow signify incorrect entries.

Day shift report for 29th has 'Done, Jane's' entry time from night shift

Night shift report for 29th has 'Do, Jone's' entry time from day shift

Below query creates a temp table with the same data as in the screenshots.

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

回答1:


First I altered your query to use CTEs rather than 3 levels of sub queries. Made it a lot easier to read.

To identify the erroneous punches I expanded the CASE statement in your query that identified duplicate punches and marked them for exclusion. Using the LEAD function I checked the next punch to see if it is within 10 minutes. If it is then it is marked for exclusion.

Obviously this solution is not perfect but gets you where you need to be with a reasonable tolerance for error. The window of allowance can be expanded or contracted.

Update: With the new data set you provided it exceeded the 10 minute allowance. I raised it to 20. Again, there has be a tolerance for error here.

I also adjusted the InOut query to not use anything with TIMEATT = 1 as a starttime. This ensures outpunches will never make it into the data set as a start time.

Last I added one more condition to the output query to make sure the starttime is on the requested report date.

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



回答2:


So Chris's answer gave me a good format to work with. I am checking to see if anyone has an entry within 4am-12pm and an exit with 12pm-7pm will be consdered day shift and anybody else will be night shift.

  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;


来源:https://stackoverflow.com/questions/55941015/how-to-distinguish-between-day-and-night-shifts-by-looking-at-past-sign-in-times

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!