简体   繁体   中英

SQL: Use value from previous row to populate current row

I have two tables table1 and table2 like so. You can see the times have gaps

table1
date      item time amount
----------------------------
1/1/2000  a    1    100
1/1/2000  a    2    100
1/1/2000  a    3    200
1/1/2000  a    6    300
1/1/2000  b    1    100
1/1/2000  b    2    100
1/1/2000  b    5    200
2/1/2000  a    1    500
2/1/2000  a    3    500
2/1/2000  a    4    550

I also have table2 where I fill the gaps

table2
date      item time amount new
-------------------------------------------
1/1/2000  a    1    100    N
1/1/2000  a    2    100    N
1/1/2000  a    3    200    N
1/1/2000  a    4           Y  <-- added amount should be 200
1/1/2000  a    5           Y  <-- added amount should be 200
1/1/2000  a    6    300    N
1/1/2000  b    1    100    N
1/1/2000  b    2    100    N
1/1/2000  b    3           Y  <-- added amount should be 100 
1/1/2000  b    4           Y  <-- added amount should be 100
1/1/2000  b    5    200    N
2/1/2000  a    1    500    N
2/1/2000  a    2    500    N
2/1/2000  a    3           Y  <-- added amount should be 500
2/1/2000  a    4    550    N

The gap row for amount should take the value of the last/previous time. I was able to identify the missing rows and add the gap rows but I tried copying the amounts to the gap rows but was not successful. I looked at what I thought were similar questions in stackoverflow and tried the solutions but it did not work such as:

update t2
set t2.amount = t1.amount
from table2 t2
inner join table1 t1 on t2.date = t1.date and t1.item = t2.item
where t2.new = 'Y'
and t2.time > (select t2.time 
              from table1 t3
              where max(t3.time) < t2.time)


update t2
set t2.amount = t1.amount
from table1 t1
inner join table2 t2 on t1.date = t2.date and t1.item = t2.item
where t2.new = 'Y' and max(t1.time) < t2.time     

Does anyone know how to access the amount for the previous row? A cursor works but that is a last resort solution. Thank you for taking the time from your busy day to help.

adding my create table code

create table #table1  (or #table2)
(
   date smalldatetime,
   item char(1),
   [time] int,
   amount int
   ,new char(1) -- for new row flag 
)

You need to find the previous non null value of amount :

update t
set amount = (
  select amount from table2 
  where
  date = t.date and item = t.item and time = (
    select max(time) from table2
    where 
    date = t.date and item = t.item 
    and time < t.time and amount is not null and new = 'N'
  ) 
)
from table2 t
where t.amount is null and t.new = 'Y'

See the demo .

you should use windowing queries:

select 
*,sum(amount) over (partition by time order by time) as previous_amount_for_null_values
from table2 

Maybe this Oracle solution will be helpful (I hope, that sql-server has something like rownum or similar solution). First, order your data and get an unique numbers (rownum pseudocolumn used). Second, for each number calculate maximal previous number witch non empty value. Join data.

Maybe there is a solution with analytical function, but no i don't have one in mind.

-- create test data
drop table tab1;
create table tab1 (
    ym varchar2(7),
    val number
);
insert into tab1 values('2016/01',3);
insert into tab1 values('2016/04',6);
insert into tab1 values('2016/08',4);
insert into tab1 values('2016/09',2);
insert into tab1 values('2016/01',5);
insert into tab1 values('2016/09',8);
insert into tab1 values('2016/05',7);
insert into tab1 values('2016/12',3);
insert into tab1(ym) values('2016/03');
insert into tab1(ym) values('2016/11');
insert into tab1(ym) values('2016/12');
insert into tab1(ym) values('2016/12');

-- solution
with q0 as (-- get rownum for each row
    select a.*, rownum as rnm -- get rownums
    from (
        select *
        from tab1
        order by ym -- order by, to further get proper order numbers from rownum
    ) a
),

q1 as (-- for each rnm get previous (maximal) rnm with non missing value
    select a.rnm, max(b.rnm) as rnm_prev
    from q0 a
    left join q0 b on a.rnm > b.rnm and b.val is not null -- get only smaller rnms with values
    group by a.rnm
)


select q0.ym, q0.rnm, q1.rnm_prev,
q0.val, pv.val as val_prev, nvl(q0.val, pv.val) as val_cor
from q0
left join q1 on q0.rnm = q1.rnm
left join q0 pv on q1.rnm_prev = pv.rnm
order by q0.rnm
update tab2
set tab2.amount=abc.PREV_AMOUNT
from
table2 tab2
join
(SELECT *,T2.AMOUNT PREV_AMOUNT
FROM TABLE1 T1
JOIN
(
SELECT
MAX(TIME) TIME,DATE,ITEM,MAX(AMOUNT) AMOUNT
FROM TABLE1
WHERE TIME!=(SELECT MAX(TIME) FROM TABLE1
GROUP BY DATE,ITEM)
GROUP BY DATE,ITEM) T2
ON T1.DATE=T2.DATE
AND T1.ITEM=T2.ITEM
AND T1.TIME=T2.TIME) abc
on tab2.date=abc.date
and tab2.item=abc.item
where tab2.new='Y' and tab2.amount is null

Breaking it down: Find the 2nd max time for each Group of Date and item. Join 2nd max time with the original table1 on Date and item to get the amount which will be prev Amount Use this prev amount to update table 2 based on Date and item wherever new ='Y'

PS: I have not run this query on sql developer. But the idea I've used should work.

You may try this...

    UPDATE T SET T.Amount = ( SELECT MAX(T2.Amount) FROM table2  T2 
    WHERE T2.Amount IS NOT NULL AND T2.[time] <= T.[time] and T2.item = T.item and t2.[date]=T.[date]
                      ) from table2  as T
      WHERE T.Amount IS NULL

mark it accepted if works fine.

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