简体   繁体   中英

Accessing previous row in SQL

I have following data :

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

Now if begin_DT is <= 9 months from the previous instance end date than I need to override the end_dt or previous row wiht end_dt of next row. I need to repeat it untill difference is <= 9

Lets calculate difference >>

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

difference in row 2 and row 3 is <=9 hence solution should be

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

Try to use LAG funcion.

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

This is a form of gaps-and-islands problem, with the islands defined by the 9 month gap. A cumulative sum of starts (based on the lag) defines the groups; then one more step gets the maximum date:

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

You say "I need to repeat it until difference is <= 9". To me that means you want to group together rows as long as the total of their gaps is not more than 9 months. I'm not sure the other answers try to do that.

You should always say what version of the Oracle database you are using. If you are using 12c or later, you can use the marvelous MATCH_RECOGNIZE clause:

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 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM