First part of the solution : as you can't call a window function inside the SET
clause of an UPDATE
, you can use a cte
instead :
WITH list AS
(
SELECT myval, LAG(myval) OVER (ORDER BY myid) AS new_val
FROM mytable
WHERE myval = 0
)
UPDATE mytable AS t
SET myval = l.new_val
FROM list AS l
WHERE t.myval = l.myval ;
Second part of the solution : for replacing the zero values with their non-zero previous value in the specified order, we can't add the clause FILTER (WHERE myval <> 0)
to the lag()
function because this clause is specific to the aggregate functions only (all the aggregate functions can be used as a window function by adding the OVER()
clause behind, but the "pure" window functions are not aggregate functions). So here we can define our own aggregate function replaced_by_first_previous_non_zero_value()
as follow :
CREATE OR REPLACE FUNCTION replaced_by_first_previous_non_zero_value(x double precision, y double precision)
RETURNS double precision LANGUAGE sql AS
$$
SELECT CASE
WHEN y = 0
THEN COALESCE(x, y)
ELSE y
END ;
$$ ;
DROP AGGREGATE IF EXISTS replaced_by_first_previous_non_zero_value(double precision) ;
CREATE AGGREGATE replaced_by_first_previous_non_zero_value(double precision)
( sfunc = replaced_by_first_previous_non_zero_value
, stype = double precision
) ;
Then, for the following query :
SELECT myval, replaced_by_first_previous_non_zero_value(myval) OVER (ORDER BY myid RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
FROM unnest(array[5, 0, 0 , 0 , 6] :: double precision[]) with ordinality as a(myval, myid)
we get he following result :
myval | replaced_by_first_previous_non_zero_value
5 | 5
0 | 5
0 | 5
0 | 5
6 | 6