PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

Ottieni il valore della colonna della riga precedente in postgres non può utilizzare la funzione della finestra in UPDATE

Ammesso che...

  • gwma_duration e duration dovrebbero essere la stessa colonna e differire a causa di errori di battitura.

  • Vuoi ordinare in base a una colonna denominata order_column . Sostituisci con le colonne effettive.

  • Le colonne della tua chiave primaria sono res_id . Sostituisci con le colonne effettive.

Metti un po' di rossetto su un maiale:

Il tuo codice procedurale è stato riparato e migliorato:

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;
  • Usa il cursore implicito di un FOR ciclo . Non c'è bisogno di un cursore esplicito ingombrante.

  • Non citare mai il nome della lingua plpgsql , che è un identificatore, non una stringa.

  • Hai semplificato la tua logica in diversi punti.

  • La cosa più importante , come ti dice il messaggio di errore, non puoi usare le funzioni della finestra in un SET clausola di un UPDATE . L'ho sostituito con una sottoquery correlata. Ma probabilmente potrebbe essere sostituito con last_duration , analogo a last_grp :ricorda solo il valore dell'ultima iterazione.

Soluzione adeguata

Tuttavia, tutto quanto sopra è molto inefficiente quando puoi farlo in un singolo UPDATE dichiarazione :

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
  • Per essere chiari:tu puoi usa le funzioni della finestra nel FROM clausola - almeno nelle versioni moderne di Postgres.

  • Usa row_number() , non rank() essere equivalente al tuo codice procedurale.