将N个工作日添加到SQL DB2

时间:2017-01-06 20:29:23

标签: sql db2

我在这里面临着一项具有挑战性的任务,花了一天时间和我只能通过一个程序来解决它但是所有项目的运行时间太长了。< / p>

如果可能的话,我想在一个查询中解决它(没有函数或过程)。

这里已经有一些问题在编程语言或sql函数/程序(我也解决了min)这样做。所以我问是否可以用SQL

来解决它

背景信息是:

  • 一个project
  • 一个phase
  • 一个holiday
  • 取消假期或周末的dayexception表格(将该日期作为工作日)并与项目相关联
  • 项目可能有0-N阶段
  • 一个阶段有start datedurationdraworder(系统需要)
  • 工作日是非周末日而非假日的所有日子(如果该日期在dayexception表格中则例外)

请考虑以下情况:

project |           phase(s)                        |  Dayexception     |  Holiday
  id    | id   pid    start     duration  draworder | pid       date    |   date
   1    | 1     1   2014-01-20     10         0     |  1     2014-01-25 | 2014-01-25
        | 2     1   2014-02-17     14         2     |                   |    

ENDDATEproject id 1的{​​{1}}实际上是phase id 1,请参阅下面生成的数据: 以下数据(现在开启)的日期格式为2014-01-31(巴西格式),值dd/mm/yyyyN

null

为了生成上述数据,我创建了一个视图proj pha start day weekday dayexcp holiday workday 1 1 20/01/2014 20/01/2014 2 N N 1 1 1 20/01/2014 21/01/2014 3 N N 1 1 1 20/01/2014 22/01/2014 4 N N 1 1 1 20/01/2014 23/01/2014 5 N N 1 1 1 20/01/2014 24/01/2014 6 N N 1 1 1 20/01/2014 25/01/2014 7 25/01/2014 25/01/2014 1 1 1 20/01/2014 26/01/2014 1 N N 0 1 1 20/01/2014 27/01/2014 2 N 27/01/2014 0 1 1 20/01/2014 28/01/2014 3 N N 1 1 1 20/01/2014 29/01/2014 4 N N 1 ,其中包含2014年和2015年的所有日期(可以更大或更小,在年度轮流情况下创建两年),如果你们是CTE查询想看到它让我知道,我会在这里添加它。以下是select语句:

daysOfYear

要解决此问题,我创建了以下查询:

select ph.project_id proj, 
       ph.id phase_id pha,
       ph.start,
       dy.curday day,
       dy.weekday, /*weekday here is a calling to the weekday function of db2*/
       doe.exceptiondate dayexcp,
       h.date holiday,
       case when exceptiondate is not null or (weekday not in (1,7) and h.date is null)
            then 1 else 0 end as workday
  from phase ph
       inner join daysofyear dy
          on (year(ph.start) = dy.year)
       left join dayexception doe
          on (ph.project_id = doe.project_id
              and dy.curday = truncate(doe.exceptiondate))
       left join holiday h
          on (dy.curday = truncate(h.date))
 where ph.project_id = 1
   and ph.id = 1
   and dy.year in (year(ph.start),year(ph.start)+1)
   and dy.curday>=ph.start
   and dy.curday<=ph.start + ((duration - 1) days)
   order by ph.project_id, start, dy.curday, draworder

这将正确返回:

select project_id, 
       min(start), 
       max(day) + sum(case when workday=0 then 1 else 0 end) days as enddate
  from project_phase_days /*(view to the above select)*/

我无法解决的问题是,我向最后一个结束日期(proj start enddate 1 20/01/2014 31/01/2014 )添加(非工作日sum(case when workday=0 then 1 else 0 end) days)的日子是周末日或假日或例外情况。

请参阅以下方案(以下阶段的持续时间为7 ):

max(day)

使用上述数据,我的查询将返回

proj   pha     start          day      weekday  dayexcp   holiday  workday
 81    578    14/04/2014    14/04/2014     2        N        N        1
 81    578    14/04/2014    15/04/2014     3        N        N        1
 81    578    14/04/2014    16/04/2014     4        N        N        1
 81    578    14/04/2014    17/04/2014     5        N        N        1
 81    578    14/04/2014    18/04/2014     6        N    18/04/2014   0
 81    578    14/04/2014    19/04/2014     7        N                 0
 81    578    14/04/2014    20/04/2014     1        N    20/04/2014   0
           /*the below data I added to show the problem*/              
 81    578    14/04/2014    21/04/2014     2        N    21/04/2014   0
 81    578    14/04/2014    22/04/2014     3        N                 1
 81    578    14/04/2014    23/04/2014     4        N                 1
 81    578    14/04/2014    24/04/2014     5        N                 1 

但正确的结果是proj start enddate 81 14/04/2014 23/04/2014 enddate,因为如果最后一天之后的天数是周末天或假期,我的查询不会考虑(或者那个例外情况)正如你在24/04/2014之上的数据集中看到的那样,我的持续时间也是假期。

我还尝试在21/04/2014表格上创建一个CTE,为每次迭代添加一天,直到持续时间结束但我无法添加phaseexceptions因为DB2不允许我在CTE递归上添加左连接。像这样:

holidays

PS:由于DB2的限制而上述查询不起作用, with CTE (projectid, start, enddate, duration, level) as ( select projectid, start, start as enddate, duration, 1 from phase where project_id=1 and phase_id=1 UNION ALL select projectid, start, enddate + (level days), duration, case when isWorkDay(enddate + (level days)) then level+1 else level end as level from CTE left join dayexception on ... left join holiday on ... where level < duration ) select * from CTE 就是一个例子(在例外日期和假日表值上就是这种情况)。

如果您有任何疑问,请在评论中提问。  任何帮助将不胜感激。感谢。

2 个答案:

答案 0 :(得分:1)

如何向前和向后计算工作日。

背景上个世纪我在这家使用这种技术的公司工作过。所以这是一个伪代码答案。它非常适合他们的目的。

您需要的是一个包含日期列和id列的表,该列以1递增。只填写表格的营业日期......由于另一个日期的观察日期,这是一个棘手的部分。就像2017-01-02是我工作的假期,但它并不是一个真正公认的假期AFAIK。

未来如何获得200个工作日。

  1. 选择日期&gt; =当前日期的分钟(id)。
  2. 选择id = id + 200的日期。
  3. 过去如何获得200个工作日。

    1. 从表中选择日期&gt; =到当前日期的最小值(ID)。
    2. 选择ID = id-200的日期。
    3. 之间的工作日。

      select count(*) from myBusinessDays where "date" between startdate and enddate
      

      祝你好运,因为这是伪代码。

答案 1 :(得分:1)

所以,使用@ danny117答案的想法,我能够创建一个查询来解决我的问题。不完全是他的想法,但它给了我解决它的方向,所以我将它标记为正确的答案,这个答案是分享实际的代码来解决它。

首先让我分享我为这些时期创建的观点。正如我所说,我创建了一个view daysofyear,其中包含2014年和2015年的数据(在我的最终解决方案中,我添加了相当大的间隔而不影响最终结果)。 Ps:此处的日期格式为巴西格式dd/mm/yyyy

 create or replace view daysofyear as 
 with CTE (curday, year, weekday) as (
      select a1.firstday, year(a1.firstday), dayofweek(a1.firstday)
        from (select to_date('01/01/1990', 'dd/mm/yyyy') firstday 
                from sysibm.sysdummy1) as a1
      union all
      select a.curday + 1 day as sumday, 
             year(a.curday + 1 day), 
             dayofweek(a.curday + 1 day)
        from CTE a
       where a.curday < to_date('31/12/2050', 'dd/mm/yyyy')
 )
 select * from cte;

使用该视图我然后在我的问题上创建了另一个视图,根据我的历史数据(更大的阶段+相当大的差距)添加了一些天数:

create or replace view project_phase_days as
select ph.project_id proj, 
       ph.id phase_id pha,
       ph.start,
       dy.curday day,
       dy.weekday, /*weekday here is a calling to the weekday function of db2*/
       doe.exceptiondate dayexcp,
       h.date holiday,
       ph.duration,
       case when exceptiondate is not null or (weekday not in (1,7) and h.date is null)
            then 1 else 0 end as workday
  from phase ph
       inner join daysofyear dy
          on (year(ph.start) = dy.year)
       left join dayexception doe
          on (ph.project_id = doe.project_id
              and dy.curday = truncate(doe.exceptiondate))
       left join holiday h
          on (dy.curday = truncate(h.date))
 where dy.year in (year(ph.start),year(ph.start)+1)
   and dy.curday>=ph.start
   and dy.curday<=ph.start + ((duration - 1) days) + 200 days 
                                            /*max duration in database is 110*/

之后我创建了这个查询:

select p.id,
       a.start,
       a.curday as enddate
  from project p left join
       (
        select p1.project_id,
               p1.duration,
               p1.start,
               p1.curday,
               row_number() over (partition by p1.project_id 
                                  order by p1.project_id, p1.start, p1.curday) rorder
          from project_phase_days p1
         where p1.validday=1
       ) as a
       on (p.id = a.project_id
          and a.rorder = a.duration)
order by p.id, a.start

它的作用是从我的视图中选择所有工作日(与我的其他日期视图一起)根据project_id排序的project_id, start date and current day (curday) rownumber然后加入project table以获取解决问题的技巧部分是a.rorder = a.duration

如果你们需要更多解释,我很乐意提供。

相关问题