我需要编写一个postgressql函数来处理一些时间序列数据,我想循环每个时间步骤,并决定是否需要更新未来几个时间步骤中的值,并且它将是更新的当循环迭代到以下时间步时,决定应用的值。我希望我已经清楚地解释了它。
因此,例如表格:
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);
我尝试将以下for循环应用于它:
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;
我的预期结果将是:
| unixdatetime | Value |
| 1 | 56 |
| 2 | 999 |
| 3 | 888 |
| 4 | 68 |
| 5 | 31 |
| 6 | 42 |
正如大家所看到的,我仍然无法切换到postgresql思维模式,仍然试图使用pyg和C ++等语言中的技巧,这些语言在postgressql中不起作用。我注意到的是:
循环中的每个语句都在整个表上执行,然后再转到循环中的下一个语句。而是在迭代到表的行之前按顺序执行所有语句。
由于1.,_counter不提供与其他语言的for循环相同的效果。我确实需要for循环中的计数器,因为实际任务要求我在前面超过2个步骤查看值。
感谢任何有用的解决方法建议。
提前致谢,
杰森
答案 0 :(得分:0)
您可以使用以下语句检索所需内容而无需循环。
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;
现在可以使用单个语句直接更新整个表:
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示例:http://sqlfiddle.com/#!15/128d9/1