Non hai bisogno di FOR LOOP
, solo un singolo AGGIORNAMENTO fa il lavoro:
UPDATE emp
SET comm = extra
WHERE comm IS NULL AND extra IS NOT NULL;
Ecco una demo:http://www.sqlfiddle.com/#!4/ aacc3/1
--- MODIFICA ----
Non ho notato che nell'output previsto deptno 10 è stato aggiornato a 20,
per aggiornare deptno
è necessaria un'altra query:
UPDATE emp
SET deptno = 20
WHERE deptno = 10;
---- MODIFICA -----
Se vuoi inserire i valori modificati nell'altra tabella, prova una procedura con RETURNING..BULK COLLECT e 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 procedura dovrebbe funzionare se non hai intenzione di elaborare un numero enorme di record in una chiamata (più di 1000 ... o al massimo alcune migliaia). Se uno dept_id
può contenere diecimila e più righe, quindi questa procedura potrebbe essere lenta, poiché consumerà un'enorme quantità di memoria PGA. In tal caso, è necessario un altro approccio con raccolta in blocco in blocchi.
-- EDIT --- come memorizzare i valori di sequenza -------
Presumo che la tabella changed
ha 4 colonne, come questa:
CREATE TABLE "TEST"."CHANGED"
( "DEPTNO" NUMBER,
"OLDVAL" NUMBER,
"NEWVAL" NUMBER,
"SEQ_NEXTVAL" NUMBER
) ;
e memorizzeremo i valori di sequenza nel seq_nextval
column.
In tal caso la procedura potrebbe essere simile a questa:
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 --- versione con cursore per piccoli insiemi di dati -----
Sì, per piccoli insiemi di dati la raccolta di massa non dà un aumento significativo della velocità e in questo caso è sufficiente un cursore semplice con for..loop.
Di seguito è riportato un esempio come utilizzare il cursore insieme ad update, notare il FOR UPDATE
clausola, è richiesta quando si prevede di aggiornare un record prelevato dal cursore utilizzando WHERE CURRENT OF
clausola.
Questa volta viene valutato un valore di sequenza all'interno dell'istruzione 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;