2010-11-25 28 views
5

我有一个包含开始时间的表(使用示例中的数字来保持简单)以及事件的持续时间。考虑行之间的“差异”对行进行分组

我想确定“块”及其开始和结束时间。
每当前一行(按开始时间排序)的结束时间(开始时间+持续时间)与当前行的开始时间之间的差值为>=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        --#   ■■■■■■■■■ 

第一块开始于04结束。由于与下一行的差异为>=5,因此在10处开始另一个块,其终止于19


我可以找出第一行的块,用LAG,但我还没有找到如何进行。

我可以在PL/SQL循环中解决这个问题,但我试图避免出于性能原因。


有关如何编写此查询的任何建议?

在此先感谢,彼得

+1

+1很酷的图形 – thomaspaulb 2010-11-25 13:04:34

回答

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

在MS-SQL,我会用ROW_NUMBER() OVER(ORDER BY starttime) AS Rank排名上开始时间的行。

然后,我会编写一个查询来将每行连接到上一行的行,并且如果差异大于5或NULL(第一行),则设置一个标志。

然后,我会选择有这个标志,该标志是开始行的所有行,并为这个子集重复编号行,并加入到下一行,以获得时间的过程跨越:

blockstarttime1 nextstarttime1 (=starttime2) 
blockstarttime2 nextstarttime2 (=starttime3) 
blockstarttime3 NULL 

最后,可以使用WHERE starttime BETWEEN blockstarttime and nextstarttime将此数据集与原始数据结合以对结果进行分区。

你来翻译这对甲骨文...

+0

谢谢!这应该可行,但它会涉及对Vincent Malgrat和MikeyByCrikey提供的解决方案避免的原始数据的另一个查询。 – 2010-11-26 07:42:23

1

有一个梦幻般的书理查德·斯诺德格拉斯这可以帮助:Developing Time-Oriented Database Applications in SQL(免费下载)随着时间的推移在数据库打交道时,我发现非常宝贵的。

查看Richards page以查看某些书籍更正链接以及zip格式的相关CD-ROM。

+0

感谢您的链接,我将阅读这些528页以后:)你检查它是否已经在[免费提供的编程书籍列表](http://stackoverflow.com/questions/194812/list-的,可自由使用的编程书籍)? – 2010-11-26 07:47:45

+0

@Peter:刚才看了一下,这本书在“SQL(Implementation agnostic)”下面列出。 – Tony 2010-11-26 07:55:57

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 
+0

谢谢!我错过了`sum(is_block_start)over``部分。接受Vincents的答案,这与您提供的查询差不多,但我发现它更易于阅读。 – 2010-11-26 07:39:30