ORACLE SQL:查找上一个最小和最大连续周期

ORACLE SQL:查找上一个最小和最大连续周期,sql,oracle,Sql,Oracle,我有下面的样本数据集,其中列出了在特定范围内(2016年1月至2018年12月)因特定原因无法工作的水表 我想有一个查询,检索最后一个最大和最小连续周期,其中仪表不在该周期范围内工作 任何帮助都将不胜感激。使用group by的聚合功能 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

我有下面的样本数据集,其中列出了在特定范围内(2016年1月至2018年12月)因特定原因无法工作的水表

我想有一个查询,检索最后一个最大和最小连续周期,其中仪表不在该周期范围内工作


任何帮助都将不胜感激。

使用group by的聚合功能

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

这有点棘手。这是一个缺口和岛屿问题。要获得所有连续期间,如果您有一个月的枚举,这将很有帮助。因此,将周期转换为月数,然后减去使用
行数()生成的序列。在相邻的几个月内,差异是恒定的

这看起来像:

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;
您有两个选择:

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
    ,该列对标志进行连续求和
  • 使用
    code
    grp
    对数据进行分组,并额外查找最大期初
  • 仅显示
    min\u period
    =
    max\u-min\u 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
说明:

  • 子查询
    数据
    仅过滤
    2016
    -
    2018
    中的行,另外将期间转换为日期格式。我们需要这个函数来增加月数
  • cte
    是递归的。锚点查找起始行,这些行具有每个代码的最大周期。union all
之后是递归成员,它查找比当前行早一个月的行。如果它找到它,则为净行,如果没有,则停止
  • 最终选择组数据。请注意,
    cte
    拒绝了不连续的时段
  • 尽管递归查询比传统查询速度慢,但在某些情况下,第二种解决方案会更好


    下面是两个查询的示例。祝你好运。

    hi fa06,我也试过了,但没有达到预期的效果。例如,对于账户0101000010,我每次得到的最短周期是201601,但最后一个最短周期应该是201610谢谢你仔细思考Stibbons:)的解释。它起作用了。我是dba新手,有许多类似的报告要处理。我现在已经理解了这一概念,其他报告也是如此。多谢各位。