Postgres 从 'bigger' 范围和 return 范围集合中删除 'smaller' 范围

Postgres removing 'smaller' ranges from a 'bigger' range and return a aggregate of ranges

我有一个可以处理双重预订的预订系统。一个人为 tsrange 预订房间,然后可以在该房间预订之上预订约会。我能够使约束正常工作,当然约会的 tsrange 必须包含在保留 tsrange 中。

现在,我需要一个查询 returns 一个 tsranges[] 范围的预订是空闲的,即当时还没有预约,但我不知道该怎么做...好吧,我对如何在 plpgsql 循环约会中实现这个有一个粗略的想法,但我想知道是否有一个更优雅的解决方案,使用普通 SQL,可能使用递归 CTE 或 window函数?

例如,假设我对范围有保留:'[2010-01-01 08:00, 2010-01-01 18:00)'

以及该预订的以下预约:'[2010-01-01 08:00, 2010-01-01 09:00)';'[2010-01-01 11:00, 2010-01-01 12:00)';'[2010-01-01 14:00, 2010-01-01 17:00)'

此类函数的输出类似于:'[2010-01-01 09:00, 2010-01-01 11:00)','[2010-01-01 12:00, 2010-01-01 14:00)','[2010-01-01 17:00, 2010-01-01 18:00)'

这是一个示例 dbfiddle,其中包含简化的架构:

create table reservation (
  id numeric,
  room_id numeric,
  during tsrange
 );
 
 create table appointment (
   id serial,
   on_reservation numeric,
   during tsrange
 );
 
 insert into reservation (id, room_id, during)
        VALUES (1, 1, '[2010-01-01 08:00, 2010-01-01 18:00)');
        
insert into appointment (id, on_reservation, during)
        VALUES (2, 1, '[2010-01-01 08:00, 2010-01-01 09:00)');

insert into appointment (id, on_reservation, during)
        VALUES (3, 1, '[2010-01-01 10:00, 2010-01-01 12:00)');

insert into appointment (id, on_reservation, during)
        VALUES (4, 1, '[2010-01-01 14:00, 2010-01-01 17:00)');

我仍然不熟悉 pg14 中添加的多范围支持,但如果这让事情变得更容易,我可以升级...

使用 PostgreSQL v14 和您的数据模型,这可以像

一样简单
SELECT r.id,
       /* convert the reservation to a multirange */
       tsmultirange(r.during)
       -
       /* aggregate the appointments to a multirange */
       range_agg(a.during)
       AS free_slots
FROM reservation AS r
   JOIN appointment AS a ON a.on_reservation = r.id
GROUP BY r.id, r.during;