Ако приемем, че ...
-
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()