我有两个表:一个计划表,其中包含有关员工如何安排的信息,以及一个数字表,其中每个数字对应一个日期。
表格如下:
[Employee Schedule]
ID Employee ID Project ID Day ID
----------- ----------- ----------- -----------
1 64 2 168
2 64 2 169
3 64 2 170
4 64 2 171
5 64 1 169
6 64 1 170
7 64 1 171
8 64 1 172
9 64 2 182
10 64 2 183
11 64 2 184
和
[Day Numbers]
ID Day
----------- ----------
168 2009-06-18
169 2009-06-19
170 2009-06-20
171 2009-06-21
172 2009-06-22
173 2009-06-23
174 2009-06-24
175 2009-06-25
176 2009-06-26
177 2009-06-27
178 2009-06-28
179 2009-06-29
180 2009-06-30
181 2009-07-01
182 2009-07-02
183 2009-07-03
184 2009-07-04
如您所见,员工64计划于2009-06-19至2009-06-22的项目1和2009-06-18至2009-06-21的项目2以及2009-07-02到2009-07-04。
我的问题是:我可以使用什么算法快速确定员工日程安排的范围,以便我可以按如下方式显示?
Employee ID Project ID Duration
----------- ---------- ------------
64 1 2009-06-19 to 2009-06-22
64 2 2009-06-18 to 2009-06-21
64 2 2009-07-02 to 2009-07-04
我可以在SQL端或代码端执行此操作。如果我需要,我可以使用Linq。该表不需要由SQL编译。这将在网站上动态发生,并应尽可能高效。如果我不需要,我不想迭代每一个并在连续的日子里寻找休息。
答案 0 :(得分:3)
假设日期ID始终是部分解决方案的顺序...
select *
from employee_schedule a
where not exists( select *
from employee_schedule b
where a.employeeid = b.employeeid
and a.projectid = b.projectid
and (a.dayid - 1) = b.dayid )
列出了开始日ID:
ID EMPLOYEEID PROJECTID DAYID
1 64 2 168
5 64 1 169
9 64 2 182
select *
from employee_schedule a
where not exists( select *
from employee_schedule b
where a.employeeid = b.employeei
and a.projectid = b.projectid
and (a.dayid + 1) = b.dayid )
列出结束日ID:
ID EMPLOYEEID PROJECTID DAYID
4 64 2 171
8 64 1 172
11 64 2 184
答案 1 :(得分:1)
让我们看一看,让事情变得更轻松:
create view EmployeeProjectDates
as
select
e.[Employee ID], e.[Project ID], d.Day
from
[Employee Scchedule] e
join [Day Numbers] d on e.[Day Id] = d.ID
您可以执行此类查询以获取所有开始日期:
select
one.[Employee ID], one.[Project ID], one.Day as StartDate
from
EmployeeProjectDays one
left join EmployeeProjectDays two on one.[Employee ID] = two.[Employee ID] and one.[Project ID] = two.[Project ID] and one.Day = DATEADD(DAY, 1, two.Day)
where
two.Day is null
然后执行类似的查询以获取结束日期并匹配它们。我认为这样的事情可以让你们两个。
select
one.[Employee ID], one.[Project ID], one.Day as StartDate,
(select
min(two_end.Day)
from
EmployeeProjectDays one_end
join EmployeeProjectDays two_end on one_end.[Employee ID] = two_end.[Employee ID] and one_end.[Project ID] = two_end.[Project ID] and one.Day = DATEADD(DAY, 1, two.Day)
where
one_end.Day is null
and two_end.Day > one.Day) as EndDate
from
EmployeeProjectDays one
left join EmployeeProjectDays two on one.[Employee ID] = two.[Employee ID] and one.[Project ID] = two.[Project ID] and one.Day = DATEADD(DAY, 1, two.Day)
where
two.Day is null
我没有测试任何这些查询,但类似的东西应该有效。在我们在应用程序代码中实现某些内容之前,我必须使用类似的查询来查找开始日期和结束日期。
答案 2 :(得分:1)
这个与oracle一起工作,从那开始它也应该可以在SQL Server中使用。 (包括测试文件)
create table schedule (id number, employee_id number, project_id number, day_id number);
insert into schedule (id, employee_id, project_id, day_id)
values(1,64,2,168);
insert into schedule (id, employee_id, project_id, day_id)
values(2,64,2,169);
insert into schedule (id, employee_id, project_id, day_id)
values(3,64,2,170);
insert into schedule (id, employee_id, project_id, day_id)
values(4,64,2,171);
insert into schedule (id, employee_id, project_id, day_id)
values(5,64,1,169);
insert into schedule (id, employee_id, project_id, day_id)
values(6,64,1,170);
insert into schedule (id, employee_id, project_id, day_id)
values(7,64,1,171);
insert into schedule (id, employee_id, project_id, day_id)
values(8,64,1,172);
insert into schedule (id, employee_id, project_id, day_id)
values(9,64,2,182);
insert into schedule (id, employee_id, project_id, day_id)
values(10,64,2,183);
insert into schedule (id, employee_id, project_id, day_id)
values(11,64,2,184);
insert into schedule (id, employee_id, project_id, day_id)
values(11,65,3,184);
select *
FROM (
select
employee_id,
project_id,
first_day,
nvl(last_day,
lead(last_day) over (
partition by employee_id, project_id
order by nvl(first_day, last_day)
)
) last_day
from (
select -- this identifies start and end rows of an interval
employee_id,
project_id,
decode (day_id - prev_day, 1, null, day_id) first_day, -- uses day_id, if prev_day is not really the previous day, i.e. a gap or null
decode (day_id - next_day, -1, null, day_id) last_day
from (
select -- this select adds columns for the previous and next day, in order to identify the boundaries of intervals
employee_id,
project_id,
day_id,
lead(day_id) over (
partition by employee_id, project_id
order by day_id
) next_day,
lag(day_id) over (
partition by employee_id, project_id
order by day_id
) prev_day
from schedule
)
)
where first_day is not null
or last_day is not null-- just filter the rows, that represent start or end dates
)
where first_day is not null
生成此输出:
64 1 169 172
64 2 168 171
64 2 182 184
65 3 184 184
答案 3 :(得分:0)
我没有测试过,但请尝试:
select [Employee ID], [Project ID], start + ' to ' + end
from (
select s.[Employee ID], s.[Project ID], min(d.Day) start, max(d.Day) end
from [Employee Schedule] s
inner join [Day Numbers] d on s.[Day ID] = d.[Day ID]
group by s.[Employee ID], s.[Project ID]
) a
编辑:更正了一些列名称
为了便于查询,我建议您将架构重构为:
[EmployeeSchedule]
ID
EmployeeID
ProjectID
StartDate
EndDate
完全摆脱日期号码。这将使您的查询更简单,更有效,并且如果您愿意,将允许您拥有NULL StartDates或EndDates的记录。