如何使用日期范围查找最重叠的时间段

时间:2014-07-14 16:27:12

标签: sql oracle date time

假设您有一个包含标识符,开始时间和结束时间的表。这些开始和结束时间可以是任何时间长度。开始时间始终在结束时间之前。假设没有空值。

什么样的查询会告诉我最“流行”的时间,即每行中的两个范围与其他大多数行重叠?

这是一个真实的应用程序,它是一个记录用户登录和退出时间的表格。我想写一个查询,它会告诉我大多数并发用户何时登录并查看这段时间。

谢谢。

2 个答案:

答案 0 :(得分:7)

有几种方法可以解决这个问题。一个使用相关子查询。这不是很有趣。相反,让我们使用累积和方法,因为你有Oracle。

关键是从一个时间戳列表开始,一个值为+1,一个开头为-1,结束时为-1。这很简单:

select t.*
from ((select starttime as thetime, 1 as value from table t) union all
      (select endtime, -1 as value from table t)
     ) t

现在,value的累积总和会告诉您在任何给定时间的活动重叠次数:

select t.*, sum(value) over (order by thetime) as numactives
from ((select starttime as thetime, 1 as value from table t) union all
      (select endtime, -1 as value from table t)
     ) t

这解决了您的问题。您可能希望在特定时间添加order by numactives desc

答案 1 :(得分:5)

以下是使用简单自联接和GROUP BY

的示例解决方案
WITH d(id, t1, t2) AS (
    SELECT 1, date '2010-01-01', date '2010-03-01' FROM DUAL UNION ALL
    SELECT 2, date '2010-02-01', date '2010-04-01' FROM DUAL UNION ALL
    SELECT 3, date '2010-02-01', date '2010-04-01' FROM DUAL UNION ALL
    SELECT 4, date '2010-01-01', date '2010-01-03' FROM DUAL UNION ALL
    SELECT 5, date '2011-01-01', date '2011-02-15' FROM DUAL
)
SELECT d1.id, d1.t1, d1.t2, 
       COUNT(*) "Overlap count", 
       LISTAGG('[' || d2.t1 || ', ' || d2.t2 || ']', ', ')
       WITHIN GROUP (ORDER BY d2.id) "Overlapping intervals"
FROM d d1 
LEFT OUTER JOIN d d2 
ON d2.t1 <= d1.t2 AND d1.t1 <= d2.t2
GROUP BY d1.id, d1.t1, d1.t2
ORDER BY COUNT(*) DESC

"Overlapping intervals"聚合仅供参考。

SQLFiddle

...带输出:

| ID | OVERLAP COUNT |                                                                          OVERLAPPING INTERVALS |
|----|---------------|------------------------------------------------------------------------------------------------|
|  1 |             4 | [01-JAN-10, 01-MAR-10], [01-FEB-10, 01-APR-10], [01-FEB-10, 01-APR-10], [01-JAN-10, 03-JAN-10] |
|  2 |             3 |                         [01-JAN-10, 01-MAR-10], [01-FEB-10, 01-APR-10], [01-FEB-10, 01-APR-10] |
|  3 |             3 |                         [01-JAN-10, 01-MAR-10], [01-FEB-10, 01-APR-10], [01-FEB-10, 01-APR-10] |
|  4 |             2 |                                                 [01-JAN-10, 01-MAR-10], [01-JAN-10, 03-JAN-10] |
|  5 |             1 |                                                                         [01-JAN-11, 15-FEB-11] |