Доступ к предыдущей строке в SQL - PullRequest
0 голосов
/ 27 июня 2019

У меня есть следующие данные:

ID Begin_Dt End_DT 
101 201205   201208
101 201301   201309
101 201401   201502
101 201701   201801

Теперь, если begin_DT составляет <= 9 месяцев с даты окончания предыдущего экземпляра, мне нужно переопределить end_dt или предыдущую строку с end_dt следующей строки. Мне нужно повторить это до разницы <= 9 </p>

Рассчитаем разницу >>

Row_num ID Begin_Dt End_DT    Diff
1   101 201205   201208    NA
2   101 201301   201309    5
3   101 201401   201502    4
4   101 201701   201801    23

разница в строке 2 и строке 3 составляет <= 9, следовательно, решение должно быть </p>

ID Begin_Dt End_DT   Flag_corr
101 201205   201502   1
101 201301   201502   1
101 201401   201502   0
101 201701   201801   0

Ответы [ 3 ]

0 голосов
/ 27 июня 2019

Это форма проблемы пробелов и островов, с островами, определяемыми разницей в 9 месяцев. Совокупная сумма стартов (на основе отставания) определяет группы; затем еще один шаг получает максимальную дату:

select t.*,
       max(end_dt) over (partition by id, grp) as new_end_dt
from (select t.*,
             sum(case when prev_end_dt >= add_months(begin_dt, -9) then 0 else 1 end) over (partition by id order by end_dt) as grp
      from (select t.*,
                   lag(end_dt) over (partition by id order by end_dt) as prev_end_dt
            from t
           ) t
     ) t
0 голосов
/ 27 июня 2019

Вы говорите: «Мне нужно повторять это, пока разница не станет <= 9». Для меня это означает, что вы хотите сгруппировать строки, если их разрывы не превышают 9 месяцев. Я не уверен, что другие ответы пытаются это сделать. </p>

Вы всегда должны указывать, какую версию базы данных Oracle вы используете. Если вы используете 12c или более позднюю версию, вы можете использовать чудесное предложение MATCH_RECOGNIZE:

with data(ID,Begin_Dt,End_DT ) as (
  select 101, to_date('201205', 'yyyymm'), to_date('201208', 'yyyymm') from dual union all
  select 101, to_date('201301', 'yyyymm'), to_date('201309', 'yyyymm') from dual union all
  select 101, to_date('201401', 'yyyymm'), to_date('201502', 'yyyymm') from dual union all
  select 101, to_date('201701', 'yyyymm'), to_date('201801', 'yyyymm') from dual
)
select * from (
  select d.*,
    months_between(
      begin_dt,
      lag(end_dt,1,begin_dt) over(partition by id order by end_dt)
    ) mon
  from data d
)
match_recognize(
  partition by id order by begin_dt
  measures final last(end_dt) new_end_dt
  all rows per match
  pattern(a b*)
  define b as sum(mon) <= 9
);

ID  BEGIN_DT          NEW_END_DT         END_DT             MON 
101 2012-05-01 00:00  2015-02-01 00:00   2012-08-01 00:00     0 
101 2013-01-01 00:00  2015-02-01 00:00   2013-09-01 00:00     5 
101 2014-01-01 00:00  2015-02-01 00:00   2015-02-01 00:00     4 
101 2017-01-01 00:00  2018-01-01 00:00   2018-01-01 00:00    23 
0 голосов
/ 27 июня 2019

Попробуйте использовать функцию LAG.

with q0 as (
    -- convert numbers to date and calculate lag
    select to_date(begin_dt,'yyyymm') as begin_dt,
    to_date(end_dt,'yyyymm') as end_dt,
    lag(to_date(end_dt,'yyyymm'),1) over(order by begin_dt) as end_dt_prev
    from dt
)


-- calculate difs and create flag
select q0.*,
months_between(end_dt,end_dt_prev) as diff,
case when months_between(end_dt,end_dt_prev) > 9 then 1 else 0 end as flag
from q0
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...