如果我们有很多任务并且每个任务的日期范围可能重叠,如何计算任务的工作天数

Posted

技术标签:

【中文标题】如果我们有很多任务并且每个任务的日期范围可能重叠,如何计算任务的工作天数【英文标题】:How to calculate number of days working on tasks if we have many tasks and the date range of each tasks could have overlap 【发布时间】:2018-12-11 06:01:55 【问题描述】:

我在工作中遇到了一个问题,如果有人能给我一些想法,我将不胜感激。

我们有一个表格,用于跟踪员工已完成的任务。表结构如下:

EmployeeNum | TaskID |Start Date of task | End Date of task

我想使用此表计算每个员工在每项任务上投入了多少天。起初我的代码如下所示:

Select 
   EmployeeNum,TaskID,DateDiff(day,StartDate,EndDate)+1 as PureDay
from
   TaskTable
Group by 
   EmployeeNum,TaskID

但后来我发现每个任务的日期范围存在重叠的问题。

例如,我们有一位员工的TaskA, TaskB, TaskC

    TaskA 是从 2018-10-01 到 2018-10-05 TaskB 从 2018-10-02 到 2018-10-07 TaskC 从 2018-10-09 到 2018-10-10

这样,该员工的实际工作日应该是从 2018-10-01 到 2018-10-07,然后是 2018-10-09 到 2018-10-10,即 9 天。如果我计算每个任务的日期范围,然后将它们加在一起,那么实际工作日将变为 5+6+2=13 天而不是 9。

如果有什么好的方法可以解决这个重叠的问题,我正在徘徊?非常感谢您的任何想法!

【问题讨论】:

您的问题的答案取决于业务规则是否规定您是否只计算一次重叠时间(或者,就此而言,是否适用其他规则)。换句话说,答案就在您的软件规范/要求中。 【参考方案1】:

以下查询将计算每个员工在每个任务上花费了多少工作日;

SELECT
    EmployeeNum,
    TaskID,
      (DATEDIFF(dd, StartDate, EndDate) + 1)
         -(DATEDIFF(wk, StartDate, EndDate) * 2)
        -(CASE WHEN DATENAME(dw, StartDate) = 'Sunday' THEN 1 ELSE 0 END)
        -(CASE WHEN DATENAME(dw, EndDate) = 'Saturday' THEN 1 ELSE 0 END) as PureDay
FROM
    TaskTable
GROUP BY
    EmployeeNum,
    TaskID

请参阅 this link 了解有关此计算如何工作的说明。

【讨论】:

您好 GMB,非常感谢您的回复,但这不是我的意思。我的问题是如何在计算员工从事任务的日历天数时摆脱重叠的日期范围。【参考方案2】:

一旦您知道任务开始的日期,您就可以使用累积总和为每条记录分配一个组,然后简单地按该组(和其他信息)进行汇总。

下面的查询应该做你想做的事:

with starts as (
      select sm.*,
             (case when exists (select 1
                                from tb_TaskMaster sm2
                                where sm2.EmpID = sm.EmpID and
                                      sm2.StartDate < sm.StartDate and
                                      sm2.EndDate >= sm.StartDate
                               )
                   then 0 else 1
              end) as isstart
      from tb_TaskMaster sm
     )

select EmpID, count(TaskId) as cnt_TaskID, min(StartDate) as StartDate, max(EndDate) as EndDate,
       datediff(Day, min(StartDate), max(EndDate)) + 1 as PureDay
from (select s.*, sum(isstart) over (partition by EmpID order by StartDate) as grp
      from starts s
     ) s
group by EmpID, grp

order by EmpID

在这个db<>fiddle 中,您可以找到我的示例数据和代码工作的 DDL 和 DML。

【讨论】:

您好 Midhun,非常感谢您的大力帮助!我从你的代码中学到了很多!真的很感激!【参考方案3】:

你可以试试这个。

我不确定它是否会一直有效,但你可以尝试一下:)

declare @table table (empid int,taskid nvarchar(50),startdate date, enddate date)

insert into @table
values
(1,'TaskA','2018-10-01','2018-10-05'),
(1,'TaskB','2018-10-02','2018-10-07'),
(1,'TaskC','2018-10-09','2018-10-10')


select *,case when comparedate > startdate then datediff(dd,comparedate,enddate) else datediff(dd,startdate,enddate)+1 end  as countofworkingdays from (
Select empid,taskid,startdate,enddate,lag(enddate,1,'1900-01-01') over(partition by empid order by startdate) as CompareDate from @table
)x

结果

【讨论】:

这太简单了,切换TaskA和B的结束日期:-) @dnoeth 你是什么意思? 当第一个范围是从 10-01 到 10-07 并且第二个范围是从 10-02 到 10-05 时,您会得到错误的结果 @dnoeth 是的。如果发生这种情况:) 没有人知道,只有查尔斯知道,但你是对的。 @Thomas,您好 Thomas,非常感谢您的帮助!这可能行得通,请让我试一试,如果我们成功了,我会告诉你的~对不起,我是 SQL 新手,我花了很长时间学习和回复。谢谢!!!【参考方案4】:

这通过根据所有之前的结束日期调整开始日期来消除重叠范围:

with maxEndDates as
 ( -- find the maximum previous end date
   Select empid,taskid,startdate,enddate,
      max(EndDate)
      over (partition by EmpID 
            order by StartDate, EndDate desc
            rows between unbounded preceding and 1 preceding)  as maxEndDate 
   from TaskTable
 ),
daysPerTask as
 ( -- calculate the difference based on the adjusted start date to eliminate overlaping days
   select *,
      case when maxEndDate >= enddate  then 0                                   -- range already fully covered
           when maxEndDate > startdate then datediff(dd, maxEndDate, enddate)   -- range partially overlapping
           else                             datediff(dd,  startdate, enddate)+1 -- new range 
      end as dayCount 
   from maxEndDates
 )
 -- get the final count
select EmpID, sum(dayCount)
from daysPerTask
group by EmpID;

见db<>fiddle

【讨论】:

【参考方案5】:

非常感谢大家的回复和帮助。我在 *** 中搜索时找到了一个解决方案,链接如下:

T-SQL date range in a table split and add the individual date to the table

Felix 在上述问题中建议的 Tally 表是解决我问题的好方法,因为我有数百万条记录,而实际情况非常复杂。

再次感谢大家的帮助!

【讨论】:

以上是关于如果我们有很多任务并且每个任务的日期范围可能重叠,如何计算任务的工作天数的主要内容,如果未能解决你的问题,请参考以下文章

查找不同行中日期时间间隔的重叠?

计算多个日期范围内有多少个重叠日期

如何减去“ Sysdate”的日期范围

PL/SQL:在由开始和结束定义的重叠日期范围内查找孤岛

java中多线程地并发运行是啥意思?有啥作用.好处?

计算日期范围的重叠数量