Consider the following table:
select id, val, newval from test1;
id | val | newval
----+-----+--------
1 | 1 | 0
2 | 2 | 0
3 | 5 | 0
4 | 9 | 0
5 | 10 | 0
I am looking for an update query that can sequentially update the values in the newval column where the updated value is the difference between val and val.prev (the value of val in the previous row) plus the UDATED value of newval from the previous row. The result would be:
select id, val, newval from test1;
id | val | newval
----+-----+--------
1 | 1 | 0 = set to zero since result would be NULL (no previous row exists)
2 | 2 | 1 = 2 - 1 + 0
3 | 5 | 4 = 5 - 2 + 1
4 | 9 | 8 = 9 - 5 + 4
5 | 10 | 9 = 10 - 9 + 8
^ - uses updated value of newval
I came close to a solution with the following:
WITH tt AS (
SELECT id, COALESCE(val - lag(val) OVER w + lag(newval) OVER w,0) as nv
FROM test1
WINDOW w AS (ORDER BY id)
)
UPDATE test1 SET newval = tt.nv FROM tt
WHERE test1.id = tt.id;
Which gives the following result:
select id, val, newval from test1;
id | val | newval
----+-----+--------
1 | 1 | 0
2 | 2 | 1 = 2 - 1 + 0
3 | 5 | 3 = 5 - 2 + 0
4 | 9 | 4 = 9 - 5 + 0
5 | 10 | 1 = 10 - 9 + 0
^ - uses old value of newval
but this solution does not use the updated values of newval. It uses the old values.
I know I can write a function to loop through the table one row at a time but from what I have read this method would be inefficient and normally discouraged. My actual application is more complicated and involves large tables so efficiency is important.
答案 0 :(得分:2)
我很确定您的逻辑可以简化为:
select id, val, (val - 1) as newval
from t;
为什么?您正在上一行中获取val
和val
之间的差异的累积和。累积差最终是最新值减去第一个值。
第一个值为1
。上面将这个值硬编码。调整第一个值的逻辑就很容易了:
select id, val,
(val - first_value(val) over (order by id) as newval
from t;