好的,这是我的尝试。
因此,如果我理解正确,除了表中明确给出的信息外,这些单位还隐含不可用。所以我首先明确地检索了这个:
select unit_id, entry_start, entry_end
from unit_calendar_entry
union
select p.id, u.entry_start, u.entry_end
from unit p
join unit_calendar_entry u
on p.parent_unit_id = u.unit_id
union
select p.parent_unit_id as unit_id, u.entry_start, u.entry_end
from unit p
join unit_calendar_entry u
on p.id = u.unit_id
and p.parent_unit_id is not null
order by unit_id, entry_start;
- 第一个
select
只是获取表中已经存在的条目
- 第二个添加了汽车的条目,因为如果它的任何一个轮胎被预订,它就可以被认为是预订的
- 第三个添加了轮胎条目,因为如果汽车被预订,它们可以被视为已预订
结果:
unit_id entry_start entry_end
----------------------------------------------------
1 2021-07-31 00:00:00 2021-08-03 00:00:00
1 2021-08-03 00:00:00 2021-08-07 00:00:00
1 2021-08-04 00:00:00 2021-08-06 00:00:00
1 2021-08-09 00:00:00 2021-08-10 00:00:00
2 2021-07-31 00:00:00 2021-08-03 00:00:00
2 2021-08-03 00:00:00 2021-08-07 00:00:00
3 2021-07-31 00:00:00 2021-08-03 00:00:00
3 2021-08-04 00:00:00 2021-08-06 00:00:00
3 2021-08-09 00:00:00 2021-08-10 00:00:00
基于此,为了对相邻/重叠的时间跨度进行分组,需要解决间隙和孤岛问题。您可以使用两个查询来标记属于一起的条目,就像在这个 SO answer 中一样。如果我们调用上面的查询subtab
,相应的语句是
select c.*, sum(case when prev_end < entry_start then 1 else 0 end) over (order by unit_id, entry_start) as grouping
from (
select subtab.*, max(entry_end) over (partition by unit_id order by entry_start rows between unbounded preceding and 1 preceding) as prev_end
from subtab
) c
- 内部查询获取每一行的前一个结尾。
- 外部分配一个分组 id(在 each 内
unit_id
)标识属于连续块(又名island)的所有条目。
结果:
unit_id entry_start entry_end prev_end grouping
-------------------------------------------------------------------------------
1 2021-07-31 00:00:00 2021-08-03 00:00:00 (null) 0
1 2021-08-03 00:00:00 2021-08-07 00:00:00 2021-08-03 00:00:00 0
1 2021-08-04 00:00:00 2021-08-06 00:00:00 2021-08-07 00:00:00 0
1 2021-08-09 00:00:00 2021-08-10 00:00:00 2021-08-07 00:00:00 1
2 2021-07-31 00:00:00 2021-08-03 00:00:00 (null) 1
2 2021-08-03 00:00:00 2021-08-07 00:00:00 2021-08-03 00:00:00 1
3 2021-07-31 00:00:00 2021-08-03 00:00:00 (null) 1
3 2021-08-04 00:00:00 2021-08-06 00:00:00 2021-08-03 00:00:00 2
3 2021-08-09 00:00:00 2021-08-10 00:00:00 2021-08-06 00:00:00 3
从此(我们称之为),您可以通过对andtab
进行分组来获得不可用的时间跨度(参见下面的这个 SO 答案或 db<>fiddle),或者按如下方式计算空闲时间:unit_id
grouping
select distinct unit_id
, NULLIF((min(ifnull(prev_end,'1000-01-01')) over (partition by unit_id, grouping)),'1000-01-01') as available_from
, min(entry_start) over (partition by unit_id, grouping) as available_til
from tab
union
select distinct unit_id
, max(entry_end) over (partition by unit_id) as available_from
, null as available_til
from tab
order by unit_id, available_from
- 第一个查询作为每个/
available_from
的最小值。为了从中获取值,我使用了类似于此 SO 答案的解决方法。prev_end
unit_id
grouping
NULL
MIN()
- 第二个查询为每个查询添加一行,
unit_id
最大值entry_end
为 start 和NULL
end
结果:
unit_id available_from available_til
---------------------------------------------------
1 (null) 2021-07-31 00:00:00
1 2021-08-07 00:00:00 2021-08-09 00:00:00
1 2021-08-10 00:00:00 (null)
2 (null) 2021-07-31 00:00:00
2 2021-08-07 00:00:00 (null)
3 (null) 2021-07-31 00:00:00
3 2021-08-03 00:00:00 2021-08-04 00:00:00
3 2021-08-06 00:00:00 2021-08-09 00:00:00
3 2021-08-10 00:00:00 (null)
将所有内容放在一个查询中:
with tab as (
select c.*, sum(case when prev_end < entry_start then 1 else 0 end) over (order by unit_id, entry_start) as grouping
from (
select d.*, max(entry_end) over (partition by unit_id order by entry_start rows between unbounded preceding and 1 preceding) as prev_end
from (
select unit_id, entry_start, entry_end
from unit_calendar_entry
union
select p.id, u.entry_start, u.entry_end
from unit p
join unit_calendar_entry u
on p.parent_unit_id = u.unit_id
union
select p.parent_unit_id as unit_id, u.entry_start, u.entry_end
from unit p
join unit_calendar_entry u
on p.id = u.unit_id
and p.parent_unit_id is not null
) d
) c
)
select distinct unit_id, NULLIF((min(ifnull(prev_end,'1000-01-01')) over (partition by unit_id, grouping)),'1000-01-01') as available_from, min(entry_start) over (partition by unit_id, grouping) as available_til
from tab
union
select distinct unit_id, max(entry_end) over (partition by unit_id) as available_from, null as available_til
from tab
order by unit_id, available_from
另请参阅此 db<>fiddle。