按连续值和获取开始日期和结束日期的Oracle查询组

按连续值和获取开始日期和结束日期的Oracle查询组,oracle,date,datetime,group-by,Oracle,Date,Datetime,Group By,我有一个这样的表实际上是一个大型查询的结果: id | date_measured | out_of_range -----+-----------------------+-------------- 3147 | 09/08/2019 20.00:00 | 1 3147 | 09/08/2019 21.00:00 | 0 3147 | 09/08/2019 22.00:00 | 0 3147 | 09/08/2019 23.00:00 | 1 3

我有一个这样的表实际上是一个大型查询的结果:

id   |  date_measured        |  out_of_range
-----+-----------------------+--------------
3147 |  09/08/2019 20.00:00  |  1
3147 |  09/08/2019 21.00:00  |  0
3147 |  09/08/2019 22.00:00  |  0
3147 |  09/08/2019 23.00:00  |  1
3147 |  10/08/2019 00.00:00  |  1
3147 |  10/08/2019 01.00:00  |  1
3147 |  10/08/2019 02.00:00  |  0
3125 |  09/08/2019 20.00:00  |  0
3125 |  09/08/2019 21.00:00  |  1
3125 |  09/08/2019 22.00:00  |  1
3125 |  09/08/2019 23.00:00  |  0
3125 |  10/08/2019 00.00:00  |  1
3125 |  10/08/2019 01.00:00  |  1
3125 |  10/08/2019 02.00:00  |  1
我需要这个结果:

id   |  date_measured_start  |  date_measured_end    |  consecutive_out_of_range
-----+-----------------------+-----------------------+--------------------------
3147 |  09/08/2019 20.00:00  |  09/08/2019 20.00:00  |  1
3147 |  09/08/2019 23.00:00  |  10/08/2019 01.00:00  |  3
3125 |  09/08/2019 21.00:00  |  09/08/2019 22.00:00  |  2
3125 |  10/08/2019 00.00:00  |  10/08/2019 02.00:00  |  3
这是值out_of_range=1以及相对开始和结束日期的连续重复

我尝试使用解决方案,但对于超出范围的部分,我不能只使用连续的1。值。

如果为每行提供两个递增的数值,则使用行数分析函数-一个为每个id,另一个为每个id/out\u范围对。如果从另一行中减去一行,则结果数字将在具有相同id/out\U of\U range值的连续行集中保持不变,您可以使用该值按以下方式分组:

查询:

选择id, 测量的最小日期作为测量的开始日期, 测量的最大日期作为测量的日期结束, 将*计为超出范围的连续\u 从…起 选择t.*, 分区上的行数按id顺序按日期测量 -按id划分的行数,按日期划分的超出范围顺序 作为注册护士 来自表\u name t 其中超出范围=1 按id分组,rn 输出:


dbfiddle

这里是与MT0答案中相同方法的不同应用。该方法称为固定差分法。在两种解决方案中,固定差分都是附加的计算值,我们根据该值对数据进行分组;也称为禁忌法

在这个解决方案中,我直接从日期中减去一个经过适当修改的行号,但只选择了标志为1的行。如果数据量非常大,但只有相对较小的一部分行具有等于1的标志,则这一点可能很重要。这是因为row_number需要对数据进行排序,而排序是一项昂贵的操作。为了解决这个问题,我们不需要按日期对标志为0的行进行排序,只需要对标志为1的行进行排序

根据MT0在此答案下方的评论进行编辑

MT0正确地指出,我的解决方案假设OP发布的测试数据中的某些内容是真实的,但没有明确说明。即,date_measured列中的日期时间是连续的日期时间序列,间隔为一小时

事实上,我的解决方案真正做到的是这一点。假设从一开始,数据仅由标记为1的超出范围的行组成,并且date_measured列中的日期时间总是四舍五入到小时,就像OP的测试数据一样。那么,问题将是确定时间连续的行序列,即相隔一小时。这就是查询的作用

结束编辑

我使用了MT0的表-来自他的db小提琴测试。谢谢MT0

with
  tabibitosan (id, date_measured, grp) as (
    select id, date_measured,
           date_measured 
           - row_number() over (partition by id order by date_measured) 
             * interval '1' hour
    from   table_name
    where  out_of_range = 1    
  )
select id, min(date_measured) as date_measured_start, 
           max(date_measured) as date_measured_end,
           count(*)           as consecutive_out_of_range
from   tabibitosan
group  by id, grp
order  by id, date_measured_start    --  or whatever
;

  ID DATE_MEASURED_START DATE_MEASURED_END CONSECUTIVE_OUT_OF_RANGE
---- ------------------- ----------------- ------------------------
3125 2019-08-09 21:00    2019-08-09 22:00                         2
3125 2019-08-10 00:00    2019-08-10 02:00                         3
3147 2019-08-09 20:00    2019-08-09 20:00                         1
3147 2019-08-09 23:00    2019-08-10 01:00                         3

在这方面没有提到的假设是,如果行在一起,那么从一行到下一行正好是一个小时的间隔;OP的样本数据就是这种情况,但如果这只是共相关,那么它可能无法在扩展的数据集上工作。如果这是一个有效的假设,那么您应该利用它来简化查询。@MT0-是的,很好。我将对此作出澄清。
with
  tabibitosan (id, date_measured, grp) as (
    select id, date_measured,
           date_measured 
           - row_number() over (partition by id order by date_measured) 
             * interval '1' hour
    from   table_name
    where  out_of_range = 1    
  )
select id, min(date_measured) as date_measured_start, 
           max(date_measured) as date_measured_end,
           count(*)           as consecutive_out_of_range
from   tabibitosan
group  by id, grp
order  by id, date_measured_start    --  or whatever
;

  ID DATE_MEASURED_START DATE_MEASURED_END CONSECUTIVE_OUT_OF_RANGE
---- ------------------- ----------------- ------------------------
3125 2019-08-09 21:00    2019-08-09 22:00                         2
3125 2019-08-10 00:00    2019-08-10 02:00                         3
3147 2019-08-09 20:00    2019-08-09 20:00                         1
3147 2019-08-09 23:00    2019-08-10 01:00                         3