for循环

时间:2016-02-04 22:20:00

标签: postgresql loops for-loop counter

我需要编写一个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. 循环中的每个语句都在整个表上执行,然后再转到循环中的下一个语句。而是在迭代到表的行之前按顺序执行所有语句。

  2. 由于1.,_counter不提供与其他语言的for循环相同的效果。我确实需要for循环中的计数器,因为实际任务要求我在前面超过2个步骤查看值。

  3. 感谢任何有用的解决方法建议。

    提前致谢,

    杰森

1 个答案:

答案 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