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

时间:2019-10-21 13:10:24

标签: oracle date datetime group-by

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

id

我需要这个结果:

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

这是值 out_of_range = 1 和相对的开始和结束日期的连续重复。

我尝试使用this解决方案,但是对于 out_of_range ,我只能没有连续的 1 。值。

2 个答案:

答案 0 :(得分:1)

如果给每一行两个递增的数值-每个ROW_NUMBER一个,另一个id / id对,则使用out_of_range分析函数。如果您从另一个中减去一个,那么在连续的行集中具有相同的id / out_of_range值的结果数将是恒定的,您可以将其用于GROUP BY

查询

SELECT id,
       MIN( date_measured ) AS date_measured_start,
       MAX( date_measured ) AS date_measured_end,
       COUNT( * ) AS consecutive_out_of_range
FROM   (
  SELECT t.*,
         ROW_NUMBER() OVER ( PARTITION BY id ORDER BY date_measured )
           - ROW_NUMBER() OVER ( PARTITION BY id, out_of_range ORDER BY date_measured )
           AS rn
  FROM   table_name t
)
WHERE out_of_range = 1
GROUP BY id, rn

输出

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

db <>提琴here

答案 1 :(得分:1)

这里是与MT0答案中相同方法的不同应用。该方法称为“固定差异”方法(两种解决方案中的“固定差异”都是将数据分组的附加计算值);也称为“塔比托聚糖”方法。

在此解决方案中,我直接从日期中减去了row_number()(经过适当修改),但之后仅选择了标志等于1的行。如果您有大量的数据,但只有相对较小的行标记等于1。这是因为row_number()需要对数据进行排序,而排序是一项昂贵的操作。为了解决这个问题,我们不需要对标志为0的行(按日期)进行排序-只需对标志为1的行进行排序即可。

编辑(基于此答案下方MT0的评论)

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

实际上,我的解决方案真正做到的是这个。假设从一开始数据就仅由超出范围的行(标志等于1)组成,并且date_measured列中的日期时间总是四舍五入,因为它们是在OP的测试数据中。那么,问题将是确定时间“连续”(意味着相隔一小时)的行的顺序。这就是查询的作用。

END EDIT

我使用了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