计算连续日期,不包括 SQL 中的周末

Posted

技术标签:

【中文标题】计算连续日期,不包括 SQL 中的周末【英文标题】:Counting consecutive dates, excluding weekends in SQL 【发布时间】:2020-03-05 15:42:52 【问题描述】:

我正在编写一份缺勤报告,其中需要包含连续缺勤 6 次的员工,同时计算给定日期范围内连续缺勤的次数(当有 6 次或更多时)。我最大的问题是处理周末。由于这是一所学校,员工周一至周五工作,因此为了计算连续 6 个日期,总会有一个周末将日期分开。我正在处理大量每天都会更新的数据,因此我无法将日期插入到临时表中来完成这项任务。我创建了一个临时表,其中存储了开始日期和结束日期之间的所有日期,不包括周末:

CREATE TABLE #TimeCardDates (
      ClientID          INT
    , EmpUID                BIGINT
    , BetweenDate           DATE
)

    ;WITH cte AS (
        SELECT 
              @StartDate StartDate
    UNION ALL
        SELECT 
              DATEADD(DAY, 1, StartDate)
        FROM cte
        WHERE StartDate < @EndDate
)
INSERT INTO #TimeCardDates (
      ClientID
    , EmpUID
    , BetweenDate
)
    SELECT 
          e.ClientID
        , e.EmpUID
        , c.StartDate
    FROM cte c
    CROSS JOIN (
        SELECT e.ClientID, e.EmpUID 
        FROM Employee e 
        JOIN #Clients c 
            ON c.ClientID = e.ClientID
    ) e

DELETE d
FROM #TimeCardDates d
    WHERE DATENAME(WEEKDAY, d.BetweenDate) IN ('Saturday','Sunday')

这是我在另一个临时表中使用的数据示例:

 **Employee**       **Description**         **DateAbsent**  **NumberOfConsecDaysAbsent**
 Employee1          Unscheduled Absence     2020-01-09
 Employee1          Unscheduled Absence     2020-01-23
 Employee1          Unscheduled Absence     2020-01-29
 Employee1          Unscheduled Absence     2020-02-05
 Employee1          Unscheduled Absence     2020-03-02
 Employee2          Unscheduled Absence     2020-01-06
 Employee2          Unscheduled Absence     2020-01-27
 Employee2          Unscheduled Absence     2020-02-07
 Employee2          Unscheduled Absence     2020-02-13
 Employee2          Unscheduled Absence     2020-02-26
 Employee3          Unscheduled Absence     2020-01-02
 Employee3          Unscheduled Absence     2020-01-03
 Employee3          Unscheduled Absence     2020-01-06
 Employee3          Unscheduled Absence     2020-01-07
 Employee3          Unscheduled Absence     2020-01-08
 Employee3          Unscheduled Absence     2020-01-09

上述数据的预期结果:

 Employee3          Unscheduled Absence                        6

有人可以帮我弄清楚怎么做吗?

【问题讨论】:

根据结果能否显示预期结果 6 个工作日转换为 8 个日历日,前提是该期间没有其他节假日。您可以直接使用 8 个日历日作为差值,这样计算起来会简单很多。 如果我理解正确,您在问题中说您已经通过将每个人都放入只有工作日的临时表来过滤掉周末,对吗?如果是这种情况,你可以使用WHERE EXISTSINTERVAL 6 DAY来检查。 【参考方案1】:

要在不包括周末的两个日期之间生成日期,您可以执行以下操作

declare @startdate date,@enddate date
 set @startdate=cast('2020-02-01' as date)
 set @enddate=cast('2020-03-01' as date)

;with num_dates
   as (select row_number() over(order by name)-1 as rnk
        from master..spt_values
      )
select @startdate   
      ,dateadd(day,rnk,@startdate) as calendar_dates 
      ,rnk
  from num_dates
 where datepart(dw,dateadd(day,rnk,@startdate)) not in (6,7)
   and dateadd(day,rnk,@startdate) <=@enddate

【讨论】:

我知道这不能回答您的问题,但这可以帮助确定您应该考虑的两个日期之间哪些天是连续的(一个警告,我假设只有那些行作为开始日期和结束日期之间的最大差异在 master..spt_values 中的记录上)【参考方案2】:

这是一个选项,使用 lag 检查前一天是否连续缺席。您可能可以将这些步骤组合成更少的 CTE,但我想将其编写成脚本,以便轻松查看逻辑是如何工作的。

注意:我为 Employee1 添加了一些额外的测试数据,以确保它在超过 6 次非连续缺勤的情况下正常工作。

create table #test (employee varchar(20), descr varchar(20), dateabsent date)

insert into #test --Create Sample Data
values
('Employee1','UnscheduledAbsence','2020-01-09'),
('Employee1','UnscheduledAbsence','2020-01-23'),
('Employee1','UnscheduledAbsence','2020-01-29'),
('Employee1','UnscheduledAbsence','2020-02-05'),
('Employee1','UnscheduledAbsence','2020-03-02'),
('Employee1','UnscheduledAbsence','2020-03-03'),
('Employee1','UnscheduledAbsence','2020-03-04'),
('Employee1','UnscheduledAbsence','2020-03-07'),
('Employee1','UnscheduledAbsence','2020-03-12'),
('Employee1','UnscheduledAbsence','2020-03-17'),
('Employee2','UnscheduledAbsence','2020-01-06'),
('Employee2','UnscheduledAbsence','2020-01-27'),
('Employee2','UnscheduledAbsence','2020-02-07'),
('Employee2','UnscheduledAbsence','2020-02-13'),
('Employee2','UnscheduledAbsence','2020-02-26'),
('Employee3','UnscheduledAbsence','2020-01-02'),
('Employee3','UnscheduledAbsence','2020-01-03'),
('Employee3','UnscheduledAbsence','2020-01-06'),
('Employee3','UnscheduledAbsence','2020-01-07'),
('Employee3','UnscheduledAbsence','2020-01-08'),
('Employee3','UnscheduledAbsence','2020-01-09')


; with CTE as (select *, datename(dw,dateabsent) as DW --Day of absence
from #test)

, cte2 as (select *
, case when lag(dateabsent) over (partition by employee order by dateabsent) = dateadd(day, -1, dateabsent) --if this absence immediately follows another absence
    or dw = 'Monday' and lag(dateabsent) over (partition by employee order by dateabsent) = dateadd(day, -3, dateabsent)  --or this is the Monday after a Friday absence
    then 1 else 0 end as Consecutive
from CTE)


, cte3 as (select *
    , sum(cte2.Consecutive) over (partition by employee order by dateabsent) + 1 as TotalConsecutiveAbsences --sum consecutive absences, add one for the current row absence
from cte2)

select cte3.employee, cte3.descr, cte3.TotalConsecutiveAbsences
from cte3
where cte3.TotalConsecutiveAbsences >= 6

编辑以清理逻辑并添加 cmets。

【讨论】:

以上是关于计算连续日期,不包括 SQL 中的周末的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server:查找学生在自定义日期内的连续缺勤计数

您如何计算 Microsoft Access SQL 查询中的连续日期?

如何计算日期但不包括微软访问中的周末?

访问 SQL 减去日期,不包括周末

计算连续日期 R

sql 连续活跃天数