【发布时间】:2017-01-10 20:12:21
【问题描述】:
我在这里面临一项具有挑战性的任务,花了一天时间,我只能通过一个程序来解决它,但运行所有项目的时间太长了。
如果可能,我想在单个查询中解决它(没有函数或过程)。
在编程语言或 sql 函数/过程中已经存在一些问题(我也解决了 min)。所以我问是否可以只用 SQL 来解决它
背景信息是:
-
project表 -
phase表 -
holiday表 -
dayexception表取消假期或周末(将该日期设为工作日)并且与项目相关联 - 一个项目可能有 0-N 个阶段
- 一个阶段有一个
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 | |
project id 1 和phase id 1 的ENDDATE 实际上是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
为了生成上述数据,我创建了一个视图 daysOfYear,其中包含 2014 年和 2015 年的所有日期(它可以更大或更小,创建它的时间为两年),如果你们想要的话,可以使用 CTE 查询看到它让我知道,我会在这里添加它。以及下面的select语句:
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
我无法解决的问题是,如果我在最后一个结束日期 (max(day)) 中添加的天数(非工作日 sum(case when workday=0 then 1 else 0 end) days)是周末、节假日或例外情况。
请看以下场景(以下阶段的持续时间为 7):
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 as 24/04/2014 这是因为我的查询没有考虑最后一天之后的日子是周末还是假期(或例外情况),如您所见在21/04/2014 上方的数据集中,在我的持续时间之外也是一个假期。
我还尝试在 phase 表上创建一个 CTE,以便为每次迭代添加一天,直到持续时间结束,但我无法添加 exceptions 或 holidays,因为 DB2 不会让我在 CTE 递归上添加左连接。像这样:
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
PS:由于 DB2 的限制,上述查询不起作用,isWorkDay 仅作为示例(可能是 dayexception 和 holiday 表值的情况)。
如果您有任何疑问,请在 cmets 中询问。 任何帮助将不胜感激。谢谢。
【问题讨论】: