PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Obtenir la valeur de la colonne de la ligne précédente dans postgres ne peut pas utiliser la fonction de fenêtre dans UPDATE

En supposant que ...

  • gwma_duration et duration sont censés être la même colonne et diffèrent en raison de fautes de frappe.

  • Vous voulez trier par une colonne nommée order_column . Remplacez par vos colonnes réelles.

  • Vos colonnes de clé primaire sont res_id . Remplacez par vos colonnes réelles.

Mettez du rouge à lèvres sur un cochon :

Votre code procédural réparé et amélioré :

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;
  • Utilisez le curseur implicite d'un FOR boucle . Pas besoin de curseur explicite encombrant.

  • Ne jamais citer le nom de la langue plpgsql , qui est un identifiant, pas une chaîne.

  • Simplifié votre logique à plusieurs endroits.

  • Le plus important , comme le message d'erreur vous l'indique, vous ne pouvez pas utiliser les fonctions de fenêtre dans un SET clause d'un UPDATE . Je l'ai remplacé par une sous-requête corrélée. Mais pourrait probablement être remplacé par last_duration , analogue à last_grp :souvenez-vous simplement de la valeur de la dernière itération.

Solution appropriée

Cependant, tout ce qui précède est très inefficace lorsque vous pouvez le faire dans un seul UPDATE déclaration :

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
  • Pour être clair :vous pouvez utiliser les fonctions de fenêtre dans le FROM clause - au moins dans les versions modernes de Postgres.

  • Utilisez row_number() , pas rank() être équivalent à votre code de procédure.