ORACLE SQL:查找最后的最小和最大连续时间

时间:2019-03-08 06:56:15

标签: sql oracle

我有以下示例数据集,其中列出了在特定范围内(2016年1月至2018年12月)由于特定原因无法使用的水表。

sample dataset

我想查询一个查询,该查询检索在该时间段内仪表不工作的最近的最大和最小连续时间段。

Expected result

任何帮助将不胜感激。

3 个答案:

答案 0 :(得分:1)

将聚合函数与分组依据一起使用

select max(mdrg_per_period) mdrg_per_period, mrdg_acc_code,max(mrdg_date_read),rea_Desc,min(mdrg_per_period) not_working_as_from
from tablename
group by mrdg_acc_code,rea_Desc

答案 1 :(得分:1)

您有两个选择:

select code, to_char(min_period, 'yyyymm') min_period, to_char(max_period, 'yyyymm') max_period
  from (
    select code, min(period) min_period, max(period) max_period,
           max(min(period)) over (partition by code) max_min_period
      from (
        select code, period, sum(flag) over (partition by code order by period) grp
          from (
            select code, period, 
                   case when add_months(period, -1) 
                             = lag(period) over (partition by code order by period) 
                        then 0 else 1 end flag
              from (select mrdg_acc_code code, to_date(mrdg_per_period, 'yyyymm') period from t)))
      group by code, grp)
  where min_period = max_min_period

说明:

  • 标记行的期间不等于上一个期间加一个月的行,
  • 创建grp列,该列连续累加标志,
  • 另外使用codegrp对数据进行分组,以找到周期的最大开始,
  • 仅显示min_period = max_min_period
  • 的行

第二个选项是Oracle 11g及更高版本中可用的递归CTE:

with 
  data(period, code) as (
    select to_date(mrdg_per_period, 'yyyymm'), mrdg_acc_code from t 
      where mrdg_per_period between 201601 and 201812),
  cte (period, code) as (
    select to_char(period, 'yyyymm'), code from data
      where (period, code) in (select max(period), code from data group by code)
    union all
    select to_char(data.period, 'yyyymm'), cte.code 
      from cte 
      join data on data.code = cte.code 
        and data.period = add_months(to_date(cte.period, 'yyyymm'), -1))
select code, min(period) min_period, max(period) max_period 
  from cte group by code

说明:

  • 子查询data仅过滤2016-2018另外将期间转换为日期格式的行。我们需要此功能才能使功能add_months正常工作。
  • cte是递归的。锚查找起始行,这些起始行的每个代码的周期最长。在union all是递归成员之后,它比当前日期早一个月 查找行。如果找到它,则排净,如果没有,则停止。
  • 最终选择组数据。请注意,cte拒绝了不连续的时间段。

尽管递归查询比传统查询要慢,但在某些情况下,第二种解决方案更好。

这是两个查询的dbfiddle demo。祝你好运。

答案 2 :(得分:0)

这有点棘手。这是一个空白问题。要获得所有连续的时间段,如果您有一个月的枚举,这将有所帮助。因此,将周期转换为月数,然后减去使用row_number()生成的序列。一组相邻月份的差异是恒定的。

这看起来像:

select acc_code, min(period), max(period)
from (select t.*,
             row_number() over (partition by acc_code order by period_num) as seqnum
      from (select t.*, floor(period / 100) * 12 + mod(period, 100) as period_num
            from t
           ) t
      where rea_desc = 'METER NOT WORKING'
     ) t
group by (period_num - seqnum);

然后,如果您想要每个帐户的最后一个,则可以使用子查询:

select t.*
from (select acc_code, min(period), max(period),
             row_number() over (partition by acc_code order by max(period desc) as seqnum
      from (select t.*,
                   row_number() over (partition by acc_code order by period_num) as seqnum
            from (select t.*, floor(period / 100) * 12 + mod(period, 100) as period_num
                  from t
                 ) t
            where rea_desc = 'METER NOT WORKING'
           ) t
      group by (period_num - seqnum)
     ) t
where seqnum = 1;