获取postgres中前一行的列值“无法在UPDATE中使用窗口函数”

时间:2014-02-10 16:26:34

标签: sql postgresql postgresql-8.4

当i大于或等于3时,我试图获取该列的前一行值并尝试在当前行计算中使用它并且我尝试使用Lag函数这样做但是没有成功,得到的错误是“无法在UPDATE中使用窗口函数”。有人可以帮助我。谢谢!

CREATE OR REPLACE FUNCTION vin_calc() RETURNS text AS
$BODY$
DECLARE
    r res%rowtype;
    i integer default 0;
    x  text;
    curs2 CURSOR FOR SELECT * FROM res;
BEGIN
open curs2;
   -- FOR r IN curs2 
  LOOP
  FETCH curs2 INTO r;
  exit when not found;

    if(x!=r.prod_grp_nm) then
    i:=0;
    end if;

     i:= i+1;

     if (i=1) then
      update res set duration =0 where 
      dur=r.dur and prod_grp_nm=r.prod_grp_nm and week_end=r.week_end;


     elsif(i=2) then
     update res set duration =1 where 
      dur=r.dur and prod_grp_nm=r.prod_grp_nm and week_end=r.week_end;


     elsif(i>=3) then
     update res set gwma_duration =0.875*lag(res.duration,1) over()+(0.125*r.dur) where 
      dur=r.dur and prod_grp_nm=r.prod_grp_nm and week_end=r.week_end;
     end if ;

    x:=r.prod_grp_nm;      

    END LOOP;
    RETURN 'yes';
END
$BODY$
LANGUAGE 'plpgsql' ;

1 个答案:

答案 0 :(得分:0)

假设......

  • gwma_durationduration应该是相同的列,因拼写错误而有所不同。

  • 您想按名为order_column的列进行排序。替换为您的实际色谱柱。

  • 您的主键列为res_id。替换为您的实际色谱柱。

在猪上涂些口红:

您的程序代码已经过修改和改进:

CREATE OR REPLACE FUNCTION vin_calc()
  RETURNS void AS
$func$
DECLARE
   r res%rowtype;
   i integer := 0;
   last_grp text;
BEGIN

FOR r IN
   SELECT * FROM res
LOOP
   IF last_grp <> r.prod_grp_nm THEN
      i := 1;
   ELSE
      i := i + 1;
   END IF;

   IF i < 3 THEN
      UPDATE res
      SET    duration = i - 1
      WHERE  dur = r.dur
      AND    prod_grp_nm = r.prod_grp_nm
      AND    week_end = r.week_end;

   ELSE
      UPDATE res r1
      SET    duration = r.dur * 0.125 + 
            (SELECT 0.875 * gwma_duration FROM res
             WHERE order_column < r1.order_column
             ORDER BY order_column
             LIMIT 1
            )  -- could be replaced with last_duration, analog to last_grp
      WHERE  r1.dur = r.dur
      AND    r1.prod_grp_nm = r.prod_grp_nm
      AND    r1.week_end = r.week_end;
   END IF;

   last_grp := r.prod_grp_nm;

   END LOOP;
END
$func$
LANGUAGE plpgsql;
  • 使用FOR loop的隐式光标。不需要笨拙的显式游标。

  • 永远不要引用语言名plpgsql,这是一个标识符,而不是字符串。

  • 在几个地方简化了你的逻辑。

  • 最重要的是,正如错误消息所示,您无法在SET的{​​{1}}子句中使用窗口函数。我用相关的子查询替换它。但可能会被替换为UPDATE,类似于last_duration:只记住上一次迭代的值。

正确的解决方案

但是,如果您可以使用last_grp语句执行此操作,则上述所有操作都非常低效:

UPDATE
  • 要明确:可以使用UPDATE res r SET duration = CASE WHEN r0.rn < 3 THEN r0.rn - 1 ELSE r0.last_dur * 0.875 + r.dur * 0.125 END FROM ( SELECT res_id, duration , row_number() OVER (PARTITION BY prod_grp_nm ORDER BY order_column) AS rn , lag(duration) OVER (PARTITION BY prod_grp_nm ORDER BY order_column) AS last_dur FROM res ) r0 WHERE r.res_id = r0.res_id 子句中的窗口函数 - 至少在现代版本的Postgres中。

  • 使用row_number(),而非 FROM 等同于您的程序代码。