sql >> Database >  >> RDS >> PostgreSQL

Kolomwaarde van vorige rij in postgre ophalen kan de vensterfunctie niet gebruiken in UPDATE

Ervan uitgaande dat ...

  • gwma_duration en duration worden verondersteld dezelfde kolom te zijn en verschillen vanwege typefouten.

  • U wilt bestellen in een kolom met de naam order_column . Vervang door uw werkelijke kolom(men).

  • Uw primaire sleutelkolommen zijn res_id . Vervang door uw werkelijke kolom(men).

Doe wat lippenstift op een varken:

Uw procedurele code gerepareerd en verbeterd:

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;
  • Gebruik de impliciete cursor van een FOR lus . Geen onhandige expliciete cursor nodig.

  • Citeer nooit de taalnaam plpgsql , wat een identifier is, geen string.

  • Vereenvoudigde uw logica op verschillende plaatsen.

  • Het belangrijkste , zoals de foutmelding aangeeft, kunt u geen vensterfuncties gebruiken in een SET clausule van een UPDATE . Ik heb het vervangen door een gecorreleerde subquery. Maar kan waarschijnlijk worden vervangen door last_duration , analoog aan last_grp :onthoud gewoon de waarde van de laatste iteratie.

Goede oplossing

Al het bovenstaande is echter erg inefficiënt als je het in een enkele UPDATE kunt doen. verklaring :

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
  • Voor alle duidelijkheid:u kunt gebruik vensterfuncties in de FROM clausule - althans in moderne versies van Postgres.

  • Gebruik row_number() , niet rank() gelijk zijn aan uw procedurele code.




  1. Wat is STATISTICS IO in SQL Server?

  2. Verbinding met mysql met netbeans voor jsp

  3. Hoe kan ik deze SQL Server SELECT-instructie voor postadres verbeteren?

  4. Zijn mysql meerdere invoegingen binnen een atomaire enkele query?