Sql 分组行时考虑;差异“;行间

Sql 分组行时考虑;差异“;行间,sql,oracle,oracle11g,Sql,Oracle,Oracle11g,我有一个包含开始时间(在示例中使用数字保持简单)和事件持续时间的表 我想确定“块”及其开始和结束时间。 当前一行(按开始时间排序)的结束时间(开始时间+持续时间)与当前行的开始时间之间的差值>=5时,应开始一个新的“块” 这是我的测试数据,包括注释中的图形解释: WITH test_data AS ( SELECT 0 s, 2 dur FROM dual UNION ALL --# ■■ SELECT 2 , 2 FROM dual UNION ALL --#

我有一个包含开始时间(在示例中使用数字保持简单)和事件持续时间的表

我想确定“块”及其开始和结束时间。
当前一行(按开始时间排序)的结束时间(开始时间+持续时间)与当前行的开始时间之间的差值
>=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循环中解决这个问题,但出于性能原因,我试图避免这样做


关于如何编写此查询有什么建议吗


提前感谢,Peter

在MS-SQL中,我将使用
行数()OVER(按开始时间排序)作为秩
来在开始时间对行进行排序

然后,我将编写一个查询,将每一行连接到具有前一个秩的行,并设置一个标志,如果差值大于5或NULL(第一行)

然后,我将选择具有此标志的所有行,这些行是起始行,对于此子集,重复对行进行编号并连接到下一行的过程,以获得时间跨度:

blockstarttime1 nextstarttime1 (=starttime2)
blockstarttime2 nextstarttime2 (=starttime3)
blockstarttime3 NULL
最后,可以使用
将此数据集连接到原始数据,其中starttime位于blockstarttime和nextstarttime之间,以对结果进行分区


由您将此翻译成Oracle…

Richard Snodgrass的一本精彩的书可能会有帮助:(免费下载),我发现这本书在处理数据库中的时间时非常宝贵


查看一些书籍更正和相关的zip格式CD-ROM的链接。

代码有点复杂,有许多子查询等。这些数据可能不起作用,但我想不出有什么现成的

处理时态数据总是一件痛苦的事情

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

我使用子查询和分析来识别和分组连续范围:

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

谢谢文森特!这正是我所需要的,我错过了
SUM(gap)而不是…
部分。次要说明:我认为可以从内部子选择中删除s的
订单
,并在必要时移动到主查询。谢谢!我在
-部分上缺少了总和(is\u block\u start)。接受文森特的回答,这或多或少与您提供的问题相同,但我发现它更容易阅读。谢谢!这应该是可行的,但它会涉及到对原始数据的另一个查询,Vincent Malgrat和MikeyByCrikey提供的解决方案避免了这一点。感谢链接,我稍后将阅读这528页:)您是否检查它是否已经在目录中?@Peter:刚刚看了一下,这本书列在“SQL(实现不可知)”下。