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;