我在这里面临着一项具有挑战性的任务,花了一天时间和我只能通过一个程序来解决它但是所有项目的运行时间太长了。< / p>
如果可能的话,我想在一个查询中解决它(没有函数或过程)。
这里已经有一些问题在编程语言或sql函数/程序(我也解决了min)这样做。所以我问是否可以用SQL
来解决它背景信息是:
project
表phase
表holiday
表dayexception
表格(将该日期作为工作日)并与项目相关联start date
,duration
和draworder
(系统需要)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 | |
ENDDATE
和project id 1
的{{1}}实际上是phase id 1
,请参阅下面生成的数据:
以下数据(现在开启)的日期格式为2014-01-31
(巴西格式),值dd/mm/yyyy
为N
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,为每次迭代添加一天,直到持续时间结束但我无法添加phase
或exceptions
因为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
就是一个例子(在例外日期和假日表值上就是这种情况)。
如果您有任何疑问,请在评论中提问。 任何帮助将不胜感激。感谢。
答案 0 :(得分:1)
如何向前和向后计算工作日。
背景上个世纪我在这家使用这种技术的公司工作过。所以这是一个伪代码答案。它非常适合他们的目的。
您需要的是一个包含日期列和id列的表,该列以1递增。只填写表格的营业日期......由于另一个日期的观察日期,这是一个棘手的部分。就像2017-01-02是我工作的假期,但它并不是一个真正公认的假期AFAIK。
未来如何获得200个工作日。
过去如何获得200个工作日。
之间的工作日。
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
如果你们需要更多解释,我很乐意提供。