PostgreSQL
 sql >> база данни >  >> RDS >> PostgreSQL

Получаване на стойност на колона от предишния ред в postgres не може да използва функцията за прозорец в UPDATE

Ако приемем, че ...

  • gwma_duration и duration трябва да са една и съща колона и да се различават поради правописни грешки.

  • Искате да подредите по колона с име 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 цикъл . Няма нужда от тромав изричен курсор.

  • Никога не цитирайте името на езика plpgsql , който е идентификатор, а не низ.

  • Опростих логиката ви на няколко места.

  • Най-важното , както ви казва съобщението за грешка, не можете да използвате прозоречни функции в SET клауза на 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
  • За да бъде ясно:вие можете използвайте прозоречни функции в FROM клауза - поне в съвременните версии на Postgres.

  • Използвайте row_number() , а не rank() да бъде еквивалентен на вашия процедурен код.




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Функции на прозореца:last_value(ORDER BY ... ASC) същите като last_value(ORDER BY ... DESC)

  2. org.postgresql.util.PSQLException:ГРЕШКА:не можа да сериализира достъпа поради зависимости за четене/запис между транзакциите

  3. Как да търсите дали цялата дума съществува в низ в Postgres

  4. Вмъкване в таблица след получаване на идентификатор от друга таблица

  5. PostgreSQL как да видите кои заявки са изпълнени