【问题标题】:postgresql multiple statements in for looppostgresql for 循环中的多个语句
【发布时间】:2016-02-04 22:19:59
【问题描述】:

我需要写一个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 思维模式,并且仍在尝试使用 python 和 C++ 等语言中的技巧,这些技巧在 postgressql 中不起作用。我注意到的是:

  1. 循环中的每个语句都在整个表上执行,然后再转到循环中的下一条语句。而是在迭代到表的行之前顺序执行所有语句。

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

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

提前致谢,

杰森

【问题讨论】:

  • create table 是错误的。那应该是 CREATE TEMPORARY TABLE currentdata as select ... 选择周围没有括号,并且 as 丢失了。
  • 这样的预期结果是什么?代码充满了错误,对我来说没有任何意义。 datatable更新后的内容应该如何?
  • 感谢您的评论,我已经编辑了原帖。我在引用我的原始代码时直接在文本窗口中输入了这个简化版本,因此请原谅错误。
  • 我不明白更新这两个值的逻辑。为什么第二行的值更新为 999,第三行的值更新为 888?您为什么不解释您要解决的实际问题,而不仅仅是转储代码。顺便说一句:如果从select 返回多于一行,IF (SELECT lead1stVal%2 FROM currentdata) = 1 应该会失败。为什么不使用循环中的记录呢?
  • 我已经将我原来代码的逻辑简化为:如果连续两行都是奇数,则将第一行更新为999,将第二行更新为888。我的印象是里面的所有内容该循环仅在当前迭代上运行,并且我找不到专门在循环内的当前迭代上调用的 postgresql 示例。希望这能澄清逻辑。我的主要障碍仍然是在迭代到下一行之前将 for 循环中的多个语句应用于单行。

标签: postgresql loops for-loop counter


【解决方案1】:

您可以使用以下语句在不需要循环的情况下检索您想要的内容。

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

【讨论】:

  • 感谢您的意见。但是,我看不到您的解决方案中更新未来时间步中值的那部分,这对我的目标至关重要,因为当我的原始循环迭代到它时,我需要对更新的值做出决定
  • @JasonTam:它根据您的示例更新表(“如果连续两行奇数,则将第一行更新为 999,第二行更新为 888") 并准确产生您的“预期结果。如果这不是您想要的,那么您的问题过于简单化了。
  • 感谢您的意见。从某种意义上说,我确实过度简化了这个问题。 “奇偶”情况与“999”和“888”值没有任何关系,但我会尽量利用你的建议。同样,正如我在 OP 中所述,我需要逻辑能够在同一个循环迭代中检查和更改超过 2 个时间戳的值,为此我仍然需要找到解决方案。
猜你喜欢
  • 2014-02-20
  • 1970-01-01
  • 2018-05-27
  • 1970-01-01
  • 2021-07-19
  • 2011-08-17
  • 2014-08-28
  • 2016-06-16
相关资源
最近更新 更多