I need to write a postgressql function to process some time-series data, which I would like to loop through each time step and decide whether the values in the few future time steps needs to be updated, and it will be the updated values that the decision is applied upon when the loop iterates to the following time steps. I hope I have explained it clearly.
Hence for example with the table:
CREATE TEMPORARY TABLE datatable (
unixdatetime integer,
value integer,
);
INSERT INTO datatable (unixdatetime, value) VALUES
(1,56),
(2,23),
(3,7),
(4,68),
(5,31),
(6,42);
I have tried to apply the following for loop to it:
FOR r IN
SELECT * FROM datatable
LOOP
DROP TABLE IF EXISTS currentdata;
CREATE TEMPORARY TABLE currentdata AS (
SELECT dt.unixdatetime as currentTime,
lead(dt.unixdatetime,1) OVER (ORDER BY dt.unixdatetime) AS lead1stTime,
dt.value AS currentValue,
lead(dt.value,1) OVER (ORDER BY dt.unixdatetime) AS lead1stVal
FROM datatable dt
);
_counter = 0;
IF (SELECT currentValue%2 FROM currentdata) = 1
THEN _counter = _counter + 1;
END IF;
IF (SELECT lead1stVal%2 FROM currentdata) = 1
THEN _counter = _counter + 1;
END IF;
UPDATE datatable dt
SET value = (CASE WHEN _counter = 2 AND cd.currentValue <> 888 THEN 999
ELSE cd.currentValue
END)
FROM currentdata cd
WHERE dt.unixdatetime = cd.currentTime;
UPDATE datatable dt
SET value = (CASE WHEN _counter = 2 THEN 888
ELSE cd.lead1stVal
END)
FROM currentdata cd
WHERE dt.unixdatetime = cd.lead1stTime;
END LOOP;
And my expected outcome would be:
| unixdatetime | Value |
| 1 | 56 |
| 2 | 999 |
| 3 | 888 |
| 4 | 68 |
| 5 | 31 |
| 6 | 42 |
As you all can see, I am still unable to switch into postgresql thinking mode, and still trying to use trick from languages such as python and C++ that doesn't work in postgressql. What I have noticed is that:
Each of these statements in the loop executes on the entire table before moving onto the next statement in the loop. instead on executing all statements sequentially before iterating onto the the row of the table.
Because of 1., the _counter does not provide the same effect like in the for loops of other languages. I do require the counter in the for loop as the actual task requires me to look into values more than 2 time steps ahead.
Any helpful suggestions of workarounds are appreciated.
Thanks in advance,
Jason
You can retrieve what you want without the need of a loop, using the following statement.
select unixdatetime, value,
case
when is_odd and next_is_odd then 999
when is_odd and prev_is_odd then 888
else value
end as new_value
from (
select unixdatetime, value,
value % 2 <> 0 as is_odd,
coalesce(lag(value) over (order by unixdatetime) % 2 <> 0, false) as prev_is_odd,
coalesce(lead(value) over (order by unixdatetime) % 2 <> 0, false) as next_is_odd
from datatable
) t
order by unixdatetime;
Now this can be used to directly update the whole table with a single statement:
update datatable
set value = nv.new_value
from (
select unixdatetime, value,
case
when is_odd and next_is_odd then 999
when is_odd and prev_is_odd then 888
else value
end as new_value
from (
select unixdatetime, value,
value % 2 <> 0 as is_odd,
coalesce(lag(value) over (order by unixdatetime) % 2 <> 0, false) as prev_is_odd,
coalesce(lead(value) over (order by unixdatetime) % 2 <> 0, false) as next_is_odd
from datatable
) t
) nv
where nv.unixdatetime = datatable.unixdatetime
and nv.new_value <> datatable.value; -- only update those that changed
SQLFiddle example: http://sqlfiddle.com/#!15/128d9/1
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.