我有一个包含开始时间的表格(在示例中使用数字来保持简单)和事件持续时间。
我想识别“块”以及它们的开始和结束时间
每当前一行的结束时间(开始时间+持续时间)(按开始时间排序)与当前行的开始时间之间的差异为>=5
时,就应该开始一个新的“块”。
这是我的测试数据,包括在评论中尝试图解说明:
WITH test_data AS (
SELECT 0 s, 2 dur FROM dual UNION ALL --# ■■
SELECT 2 , 2 FROM dual UNION ALL --# ■■
SELECT 10 , 1 FROM dual UNION ALL --# ■
SELECT 13 , 4 FROM dual UNION ALL --# ■■■■
SELECT 15 , 4 FROM dual --# ■■■■
)
--# Should return
--# 0 .. 4 --# ■■■■
--# 10 .. 19 --# ■■■■■■■■■
第一个块从0
开始,到4
结束。由于与下一行的差异为>=5
,因此请在10
处开始另一个以19
结尾的块。
我可以使用LAG
识别一个块的第一行,但我还没有找到如何继续。
我可以在PL / SQL循环中解决问题,但出于性能原因,我试图避免这种情况。
有关如何编写此查询的任何建议吗?
先谢谢,彼得
答案 0 :(得分:3)
我使用带有分析的子查询来识别和分组连续范围:
SQL> WITH test_data AS (
2 SELECT 0 s, 2 dur FROM dual UNION ALL --# ■■
3 SELECT 2 , 2 FROM dual UNION ALL --# ■■
4 SELECT 10 , 1 FROM dual UNION ALL --# ■
5 SELECT 13 , 4 FROM dual UNION ALL --# ■■■■
6 SELECT 15 , 4 FROM dual --# ■■■■
7 )
8 SELECT MIN(s) "begin", MAX(s + dur) "end"
9 FROM (SELECT s, dur, SUM(gap) over(ORDER BY s) my_group
10 FROM (SELECT s, dur,
11 CASE
12 WHEN lag(s + dur) over(ORDER BY s) >= s - 5 THEN
13 0
14 ELSE
15 1
16 END gap
17 FROM test_data
18 ORDER BY s))
19 GROUP BY my_group;
begin end
---------- ----------
0 4
10 19
答案 1 :(得分:2)
代码变得有点复杂,有许多子查询等。可能是数据的实例,这不起作用,但我不能想到任何超出我的头脑。
使用时态数据总是很痛苦!
WITH test_data AS (
SELECT 0 s, 2 dur FROM dual UNION ALL --# ■■
SELECT 2 , 2 FROM dual UNION ALL --# ■■
SELECT 10 , 1 FROM dual UNION ALL --# ■
SELECT 13 , 4 FROM dual UNION ALL --# ■■■■
SELECT 15 , 4 FROM dual --# ■■■■
)
select
-- Group on each block
min(start_time) as s,
max(end_time) - min(start_time) as dur
from (
select
start_time,
duration,
end_time,
-- number the blocks sequentially
sum(is_block_start) over (order by start_time) as block_num
from (
select
start_time,
duration,
end_time,
-- Mark the start of each block
case
when nvl2(prev_end_time, start_time - prev_end_time,5) >= 5
then 1 else 0 end as is_block_start
from (
select
s as start_time,
dur as duration,
s+dur as end_time,
lag(s+dur) over (order by s) prev_end_time
from test_data
)
)
)
group by block_num
答案 2 :(得分:1)
在MS-SQL中,我会使用ROW_NUMBER() OVER(ORDER BY starttime) AS Rank
在开始时间对行进行排名。
然后,我会编写一个查询,将每行连接到具有先前Rank的行,如果差值大于5或NULL(第一行),则设置一个标志。
然后,我将选择具有此标志的所有行作为起始行,并且对于此子集重复编号行并连接到下一行以获得时间跨度的过程:
blockstarttime1 nextstarttime1 (=starttime2)
blockstarttime2 nextstarttime2 (=starttime3)
blockstarttime3 NULL
最后,可以使用WHERE starttime BETWEEN blockstarttime and nextstarttime
将此数据集连接到原始数据,以对结果进行分区。
由您决定将其转换为Oracle ...
答案 3 :(得分:1)
理查德·斯诺德格拉斯(Richard Snodgrass)有一本很棒的书可能会有所帮助:Developing Time-Oriented Database Applications in SQL(免费下载),我在数据库中处理时间时发现它非常宝贵。
在Richards page上查看一些图书更正的链接以及zip格式的相关CD-ROM。