ORACLE SQL: найти последний минимальный и максимальный последовательный период - PullRequest
0 голосов
/ 08 марта 2019

Ниже приведен пример данных, в котором перечислены счетчики воды, не работающие по определенной причине в течение определенного периода (январь 2016 г. - декабрь 2018 г.).

sample dataset

Я хотел бы получить запрос, который извлекает последний максимальный и минимальный последовательный период, когда счетчик не работал в этом диапазоне.

Expected result

любая помощь будет принята с благодарностью.

Ответы [ 3 ]

1 голос
/ 08 марта 2019

У вас есть два варианта:

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_period = max_min_period

Второй вариант - рекурсивный CTE, доступный в Oracle 11g и выше:

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 для обоих запросов. Удачи.

1 голос
/ 08 марта 2019

использовать агрегатную функцию с группой по

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
0 голосов
/ 08 марта 2019

Это немного сложно.Это проблема разрыва и островов.Чтобы получить все непрерывные периоды, это поможет, если у вас есть перечисление месяцев.Итак, преобразуйте период в число месяцев, а затем вычтите последовательность, сгенерированную с помощью 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;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...