【问题标题】:How to distinguish between day and night shifts by looking at past sign in times?如何通过查看过去的签到时间来区分白天和夜班?
【发布时间】:2019-09-20 07:13:04
【问题描述】:

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

29 日的白班报告已从夜班开始“完成,简的”进入时间

29 日的夜班报告中包含“Do,Jone's”从白班开始的进入时间

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

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

【问题讨论】:

  • 我尝试创建一个 SQL 小提琴,但由于某种原因,我收到一条错误消息,指出需要声明 @shift。 sqlfiddle.com/#!18/053107/1
  • 在执行您的代码时收到错误消息数据类型 date 和 datetime 在 add 运算符中不兼容
  • 对不起,我错误地将问题标记为 2012。但是服务器版本是 MS SQL Server 2014。您使用的是什么版本?
  • 使用 SSMS 18 针对 2012 年和 2016 年进行了测试。
  • sqlfiddle.com/#!18/2e4e1/1 我确实设法让它在这里运行

标签: sql tsql sql-server-2014 ssrs-2012


【解决方案1】:

首先,我将您的查询更改为使用 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

【讨论】:

  • 我尝试在这里运行它sqlfiddle.com/#!18/964b4/1。一名白班员工仍然潜入夜班,这是我试图避免的。
  • 在上面的 sql fiddle 中只有 Jone Night 应该出现。
  • 答案已更新以适应新的数据集。
  • sqlfiddle.com/#!18/964b4/14。嗨,克里斯,我应该提到更多关于这个问题的规则。由于我无法准确指定错误窗口。我已经在一天中的指定时间寻找进入和退出。如果有入口和出口,那么员工将是白班。
【解决方案2】:

所以克里斯的回答给了我一个很好的工作格式。我正在检查是否有人在凌晨 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;

【讨论】:

    猜你喜欢
    • 2018-11-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-25
    • 2016-04-08
    • 1970-01-01
    • 2018-02-15
    相关资源
    最近更新 更多