以下使用了@Brits 提到的大部分技术。他们提供了一些非常好的信息,所以我不会重复,但建议您查看它(和链接)。然而,我确实采取了稍微不同的方法。首先是几个表更改。我使用 ISO 第 1-7 周(周一至周日)的日期而不是日期名称。日期名称很容易被提取出来供日后使用。我也使用间隔代替开始和结束时间的时间。(时间数据类型适用于大多数情况,但有一种情况不适用(稍后会详细介绍)。
您的描述不清楚的一件事是结束时间是否包含在可用时间中。如果包括最后一个时间间隔将是 11:00-11:15。如果排除最后一个时间间隔是 10:45-11:00。我假设排除它。在最终结果中,结束时间被解读为“最多但不包括”。
-- setup
create table availabilities (weekday integer, start_time interval, end_time interval);
insert into availabilities (weekday , start_time , end_time )
select wkday
, start_time
, end_time
from (select *
from (values (1, '09:00'::interval, '11:00'::interval)
, (1, '13:00'::interval, '17:00'::interval)
, (2, '08:00'::interval, '17:00'::interval)
, (3, '08:30'::interval, '10:45'::interval)
, (4, '10:30'::interval, '12:45'::interval)
) as v(wkday,start_time,end_time)
) r ;
select * from availabilities;
以 CTE (next_week) 开头的查询为从星期一开始的一周中的每一天生成一个条目,并为其生成相应的 ISO 天数。主查询将这些与可用性表连接起来,以获取匹配日期的时间。最后,该结果与生成的时间戳交叉连接以获得 15 分钟的间隔。
-- Main
with next_week (wkday,tm) as
(SELECT n+1, date_trunc('week', current_date) + interval '1 week' + n*interval '1 day'
from generate_series (0, 6) n
)
select to_char(gdtm,'Day'), gdtm start_time, gdtm+interval '15 min' end_time
from ( select wkday, tm, start_time, end_time
from next_week nw
join availabilities av
on (av.weekday = nw.wkday)
) s
cross join lateral
generate_series(start_time+tm, end_time+tm- interval '1 sec', interval '15 min') gdtm ;
异常值
如前所述,在一种情况下时间数据类型不能令人满意,但您可能不需要它。当轮班工人说他们有空的时间是 23:00-01:30 时会发生什么。相信我,当轮班工人在周五 22:00 上班时,01:30 仍然是周五晚上,即使日历可能不一致。(我工作了很多年。)以下使用间隔处理了这个问题。加载与之前相同的数据,并为此案例添加内容。
insert into availabilities (weekday, start_time, end_time )
select wkday
, start_time
, end_time + case when end_time < start_time
then interval '1 day'
else interval '0 day'
end
from (select *
from (values (1, '09:00'::interval, '11:00'::interval)
, (1, '13:00'::interval, '17:00'::interval)
, (2, '08:00'::interval, '17:00'::interval)
, (3, '08:30'::interval, '10:45'::interval)
, (5, '23:30'::interval, '02:30'::interval) -- Friday Night - Saturday Morning
) as v(wkday,start_time,end_time)
) r
;
select * from availabilities;
希望这可以帮助。