【问题标题】:Oracle query group by consecutive value and get start date and end dateOracle查询按连续值分组并获取开始日期和结束日期
【发布时间】:2019-10-21 13:10:24
【问题描述】:

我有一张这样的表(其实是大查询的结果):

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 和相对开始和结束日期的连续重复。

我尝试使用 this 解决方案,但我不能只为 out_of_range 设置连续的 1。价值。

【问题讨论】:

    标签: oracle date datetime group-by


    【解决方案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
    

    输出

    身份证 | 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

    【讨论】:

      【解决方案2】:

      这是与 MT0 答案中相同方法的不同应用。该方法被称为“固定差异”方法(两种解决方案中的“固定差异”是我们对数据进行分组的附加计算值);也称为“tabibitosan”方法。

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

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

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

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

      结束编辑

      我使用了 MT0 的表格 - 来自他的 db fiddle 测试。谢谢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 - 是的,好点。我将对此进行澄清。
      猜你喜欢
      • 2021-09-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多