假设您有一个包含标识符,开始时间和结束时间的表。这些开始和结束时间可以是任何时间长度。开始时间始终在结束时间之前。假设没有空值。
什么样的查询会告诉我最“流行”的时间,即每行中的两个范围与其他大多数行重叠?
这是一个真实的应用程序,它是一个记录用户登录和退出时间的表格。我想写一个查询,它会告诉我大多数并发用户何时登录并查看这段时间。
谢谢。
答案 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"
聚合仅供参考。
...带输出:
| 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] |