Как отличить дневные и ночные смены, глядя на прошедшие знаки времени? - PullRequest
3 голосов
/ 01 мая 2019

В настоящее время в отчете используется предварительно определенное время. Но иногда сотрудники просрочивают свою смену и просматривают отчет о следующей смене. Как мне изменить запрос к отчету, чтобы посмотреть на прошедший знак времени, чтобы избежать этого? В столбце TIMEATT 1 - запись, а 2 - выход. Голубой цвет выделяет правильные записи, а желтый обозначает неправильные записи.

В отчете о дневной смене за 29-е время «Готово, Джейн» вступает из ночной смены. Day shift report for 29th has Done, Janes entry time from night shift

Отчет о ночной смене для 29-го имеет время входа «До, Джона» из дневной смены Night shift report for 29th has Do, Jones entry time from day shift

При запросе ниже создается временная таблица с теми же данными, что и на снимках экрана.

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

Ответы [ 2 ]

1 голос
/ 02 мая 2019

Сначала я изменил ваш запрос, чтобы использовать CTE, а не 3 уровня подзапросов. Сделано это намного легче читать.

Чтобы определить ошибочные штампы, я расширил оператор CASE в вашем запросе, который идентифицировал дублирующие штампы и пометил их для исключения. Используя функцию LEAD, я проверил следующий удар, чтобы убедиться, что он в течение 10 минут. Если это так, он помечается для исключения.

Очевидно, что это решение не идеально, но оно доставит вас туда, где вам нужно, с разумной терпимостью к ошибкам. Окно пособия может быть расширено или сокращено.

Обновление: с предоставленным вами новым набором данных он превысил 10-минутный допуск. Я поднял его до 20. Опять же, здесь есть допуск к ошибке.

Я также настроил запрос InOut, чтобы он не использовал ничего с TIMEATT = 1 в качестве времени запуска. Это гарантирует, что выходы никогда не попадут в набор данных как время начала.

В заключение я добавил еще одно условие к выходному запросу, чтобы убедиться, что время начала соответствует запрошенной дате отчета.

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
0 голосов
/ 03 мая 2019

Так что ответ Криса дал мне хороший формат для работы. Я проверяю, есть ли у кого-нибудь вход в 4: 00-12: 00, и выход с 12: 00-19: 00 будет сдан в дневную смену, а кто-либо еще будет в ночную смену.

  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;
...