简体   繁体   English

PostgreSQL for循环中的多个语句

[英]postgresql multiple statements in for loop

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. 我需要编写一个postgressql函数来处理一些时间序列数据,我想遍历每个时间步长并确定是否需要更新未来几个时间步长中的值,并且它将是更新后的值当循环迭代到以下时间步长时,将应用决策。 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循环应用于它:

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. 大家都知道,我仍然无法切换到Postgresql思维模式,并且仍然尝试使用python和C ++等在postgressql中不起作用的语言的技巧。 What I have noticed is that: 我注意到的是:

  1. 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. 而是依次执行所有语句,然后再迭代到表的行。

  2. Because of 1., the _counter does not provide the same effect like in the for loops of other languages. 由于1。,_ counter不能提供与其他语言的for循环相同的效果。 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. 我确实需要for循环中的计数器,因为实际任务需要我提前2个时间步长查看值。

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 SQLFiddle示例: http ://sqlfiddle.com/#!15/128d9/1

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM