鉴于这两个表:
[dbo].[Task]
[Id] [Duration] [ScheduledStart]
int int Nullable DateTime
[dbo].[TaskDependencies]
[Id] [PredecessorTaskId] [TaskId]
int FK_Task_Id FK_Task_Id
我正在尝试找到Tasks前任的最新结束日期。对于Task表,ScheduledStart可以为空。这背后的想法是,如果它没有明确安排的启动,它可以从它的前辈派生(根级任务必须有一个ScheduledStart,所以计算可以从某处开始)。任务也可以有多个前辈。
我想出了一个假的递归函数(我认为)可以完成我正在寻找的东西。我想知道的是,如果有更有效的方法在SQL中编写它,因为我已经习惯了更多的程序编程。我应该只将Function_A作为存储过程并让它自己调用吗?有没有办法可以用WITH语句来完成(并且那些是一种更有效的递归查询方式,或者像这样的递归函数)?
DateTime Function_A(Task)
{
var predecessorList = getPredecessors(Task)
var latestEndDate;
var currentPredecessorLatestEndDate;
ForEach(Predecessor in predecessorList)
{
if(Predecessor.ScheduledStart != null)
{
if(latestEndDate != null)
{
if(Predecessor.StartDate + Predecessor.Duration > latestEndDate)
{
latestEndDate = Predecessor.StartDate + Predecessor.Duration;
}
}
else
{
latestEndDate = Predecessor.StartDate + Predecessor.Duration;
}
}
else
{
currentPredecessorLatestEndDate = Function_A(Predecessor.Id);
if(latestEndDate != null)
{
if(currentPredecessorEndDate > latestEndDate)
{
latestEndDate = currentPredecessorEndDate;
}
}
else
{
latestEndDate = currentPredecessorEndDate;
}
}
}
return latestEndDate;
}
感谢您的帮助!
答案 0 :(得分:1)
您可以使用递归CTE查找所有上游任务。然后,您可以根据上一个结束子任务计算第一个可用的开始日期。如果任务具有本身具有未知开始日期的子任务,则需要多次传递。
使用递归CTE的示例代码:
;with Predecessors as
(
select Id as RootId
, null as ChildId
from @Task
union all
select p.RootId
, cd.PredecessorTaskId as ChildId
from @TaskDependencies cd
join Predecessors p
on cd.TaskId = ISNULL(p.ChildId, p.RootId)
)
select RootId
, max(dateadd(day, c.Duration+1, c.ScheduledStart))
from Predecessors p
join @Task c
on p.ChildId = c.Id
-- Filter out tasks with child tasks that themselves have
-- an unknown start date.
where not exists
(
select *
from Predecessors p2
join @Task c2
on p2.ChildId = c2.Id
where p2.RootId = p.RootId
and c2.ScheduledStart is null
)
group by
RootId
测试数据:
declare @Task table (Id int, Duration int, ScheduledStart datetime)
insert @Task
select 1, 3, '2010-01-01'
union all select 2, 3, '2010-01-03'
union all select 3, 3, null
union all select 4, 3, '2010-01-01'
union all select 5, 3, null
declare @TaskDependencies table (PredecessorTaskId int, TaskId int)
insert @TaskDependencies
select 1, 3
union all select 2, 3
union all select 4, 5
union all select 3, 5
打印:
3 2010-01-07
它过滤掉任务5,其中包含具有未知开始日期的子任务。如果输入任务3的计算开始日期,则可以计算任务5的开始日期。