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

mettre à jour en utilisant la boucle for dans plsql

Vous n'avez pas besoin de FOR LOOP , une seule MISE À JOUR fait le travail :

UPDATE emp
  SET comm = extra
WHERE comm IS NULL AND extra IS NOT NULL;

Voici une démo :http://www.sqlfiddle.com/#!4/ aacc3/1

--- MODIFIER ----

Je n'ai pas remarqué que, dans la sortie attendue, deptno 10 a été mis à jour à 20,
pour mettre à jour deptno une autre requête est nécessaire :

UPDATE emp
   SET deptno = 20
WHERE deptno = 10;



---- MODIFIER -----

Si vous souhaitez insérer des valeurs modifiées dans l'autre table, essayez une procédure avec RETURNING..BULK COLLECT et FORALL :

CREATE OR REPLACE PROCEDURE pro_cedure( p_dept_id number  ) 
IS
      TYPE changed_table_type IS TABLE OF changed%ROWTYPE;
      changed_buff changed_table_type;
BEGIN
      SELECT deptno, comm, extra BULK COLLECT INTO changed_buff
      FROM emp
      WHERE comm IS NULL AND extra IS NOT NULL AND deptno = p_dept_id
      FOR UPDATE;
      UPDATE emp
      SET comm = extra
      WHERE comm IS NULL AND extra IS NOT NULL AND deptno = p_dept_id;
      FORALL i IN 1 .. changed_buff.count
        INSERT INTO changed VALUES changed_buff( i );
END;
/

La procédure devrait fonctionner si vous n'allez pas traiter un grand nombre d'enregistrements en un seul appel (plus de 1000 ... ou quelques milliers au maximum). Si un dept_id peut contenir des dizaines de milliers de lignes et plus, alors cette procédure peut être lente, car elle consommera une énorme quantité de mémoire PGA. Dans un tel cas, une autre approche avec une collecte en masse en morceaux est nécessaire.

-- EDIT --- comment stocker les valeurs de séquence -------

Je suppose que la table changed a 4 colonnes, comme ceci :

  CREATE TABLE "TEST"."CHANGED" 
   (    "DEPTNO" NUMBER, 
        "OLDVAL" NUMBER, 
        "NEWVAL" NUMBER, 
        "SEQ_NEXTVAL" NUMBER 
   ) ;

et nous stockerons les valeurs de séquence dans le seq_nextval colonne.

Dans ce cas, la procédure peut ressembler à ceci :

create or replace 
PROCEDURE pro_cedure( p_dept_id number  ) 
IS
      TYPE changed_table_type IS TABLE OF changed%ROWTYPE;
      changed_buff changed_table_type;
BEGIN
      SELECT deptno, comm, extra, sequence_name.nextval 
        BULK COLLECT INTO changed_buff
        FROM emp
        WHERE comm IS NULL AND extra IS NOT NULL AND deptno = p_dept_id
        FOR UPDATE;
      UPDATE emp
        SET comm = extra
        WHERE comm IS NULL AND extra IS NOT NULL AND deptno = p_dept_id;
      FORALL i IN 1 .. changed_buff.count
        INSERT INTO changed VALUES changed_buff( i );
END;



--- EDIT --- version avec curseur pour petits ensembles de données -----

Oui, pour de petits ensembles de données, la collecte en masse ne donne pas une augmentation significative de la vitesse, et un curseur simple avec for..loop est suffisant dans un tel cas.
Vous trouverez ci-dessous un exemple comment utiliser le curseur avec la mise à jour, notez le FOR UPDATE clause, elle est requise lorsque nous prévoyons de mettre à jour un enregistrement extrait du curseur en utilisant WHERE CURRENT OF clause.
Cette fois, une valeur de séquence est évaluée dans l'instruction INSERT.

create or replace 
PROCEDURE pro_cedure( p_dept_id number  ) 
IS
      CURSOR mycursor IS 
         SELECT deptno, comm, extra
         FROM emp
         WHERE comm IS NULL AND extra IS NOT NULL 
               AND deptno = p_dept_id
         FOR UPDATE;    
BEGIN
      FOR emp_rec IN  mycursor
      LOOP
         UPDATE emp 
            SET comm = extra
            WHERE CURRENT OF mycursor;
         INSERT INTO changed( deptno, oldval, newval, seq_nextval)
                VALUES( emp_rec.deptno, emp_rec.comm, 
                        emp_rec.extra, sequence_name.nextval );
      END LOOP;
END;