我用一个例子来解释它。我们有5个事件(每个事件都有一个开始日期和结束日期),部分重叠:
create table event (
id integer primary key,
date_from date,
date_to date
);
--
insert into event (id, date_from, date_to) values (1, to_date('01.01.2016', 'DD.MM.YYYY'), to_date('03.01.2016 23:59:59', 'DD.MM.YYYY HH24:MI:SS'));
insert into event (id, date_from, date_to) values (2, to_date('05.01.2016', 'DD.MM.YYYY'), to_date('08.01.2016 23:59:59', 'DD.MM.YYYY HH24:MI:SS'));
insert into event (id, date_from, date_to) values (3, to_date('03.01.2016', 'DD.MM.YYYY'), to_date('05.01.2016 23:59:59', 'DD.MM.YYYY HH24:MI:SS'));
insert into event (id, date_from, date_to) values (4, to_date('03.01.2016', 'DD.MM.YYYY'), to_date('03.01.2016 23:59:59', 'DD.MM.YYYY HH24:MI:SS'));
insert into event (id, date_from, date_to) values (5, to_date('05.01.2016', 'DD.MM.YYYY'), to_date('07.01.2016 23:59:59', 'DD.MM.YYYY HH24:MI:SS'));
--
commit;
这里可视化的事件:
1.JAN 2.JAN 3.JAN 4.JAN 5.JAN 6.JAN 7.JAN 8.JAN
---------1--------- ------------2-------------
---------3---------
--4-- ---------5---------
现在我想选择在给定时间范围内重叠的最大事件数。
对于时间范围01.01.2016 00:00:00 - 08.01.2016 23:59:59,结果应该是3,因为最多3个事件重叠(在03.01.2016 00:00:00 - 03.01.2016 23:59之间) :59和05.01.2016 00:00:00 - 05.01.2016 23:59:59之间。
对于时间范围06.01.2016 00:00:00 - 08.01.2016 23:59:59,结果应为2,因为最多2个事件重叠(2016年1月16日00:00:00 - 07.01.2016 23:59之间:59)
SQL中是否有(高效)解决方案?我正在考虑性能,因为在很宽的时间范围内可能会有很多事件。
更新#1
我最喜欢MTO的答案。它甚至适用于时间范围01.01.2016 00:00:00 - 01.01.2016 23:59:59。我根据我的确切需求调整了SQL:
select max(num_events)
from (
select sum(startend) over (order by dt) num_events
from (
select e1.date_from dt,
1 startend
from event e1
where e1.date_to >= :date_from
and e1.date_from <= :date_to
union all
select e2.date_to dt,
-1 startend
from event e2
where e2.date_to >= :date_from
and e2.date_from <= :date_to
)
);
答案 0 :(得分:2)
这将获得所有时间范围和在这些范围内发生的事件计数:
SELECT *
FROM (
SELECT dt AS date_from,
LEAD( dt ) OVER ( ORDER BY dt ) AS date_to
SUM( startend ) OVER ( ORDER BY dt ) AS num_events
FROM (
SELECT date_from AS dt, 1 AS startend FROM event
UNION ALL
SELECT date_to, -1 FROM event
)
)
WHERE date_from < date_to;
答案 1 :(得分:0)
如果您只需要获取数字而不需要使用更精确的时间值进行操作,那么它就是这样的:
SELECT MAX(c) max_overlap FROM
(SELECT d, COUNT(1) c
FROM
(SELECT date_from d FROM event
UNION ALL
SELECT date_to FROM event
) A
GROUP BY A.d
) B
否则它将需要使用递归等。
答案 2 :(得分:0)
您必须将问题分解为几个子问题:
您可以尝试以下查询,其中这些子问题在with-statements(公用表表达式)中建模:
with myinterval as (
select to_date('2016-01-01 0:00:00', 'yyyy-mm-dd hh24:mi:ss') as date_from,
to_date('2016-01-08 23:59:59', 'yyyy-mm-dd hh24:mi:ss') as date_to
from dual
), affected_events as (
select *
from event e
where wm_overlaps(
wm_period(myinterval.date_from, myinterval.date_to),
wm_period(e.date_from, e.date_to)
) = 1
), starts as (
select distinct date_from from affected_events
), overlapped as (
select starts.date_from, count(*) as cnt
from affected_events ae
join starts on (wm_overlaps(wm_period(starts.date_from, starts.date_from+0.001), wm_period(ae.date_from, ae.date_to))= 1)
group by starts.date_from
)
select max(cnt) from overlapped
答案 3 :(得分:0)
这将返回事件重叠的所有峰值,包括峰值开始和结束。
select distinct
max(e1.date_from),
case when
min(e1.date_to) < min(e2.date_to)
then
min(e1.date_to)
else
min(e2.date_to)
end,
count(1) + 1
from event e1 inner join event e2 on (e2.date_from <= e1.date_from and e2.date_to >= e1.date_from and e1.id != e2.id)
group by e1.ID;