Você não precisa de
FOR LOOP
, apenas um único UPDATE faz o trabalho:UPDATE emp
SET comm = extra
WHERE comm IS NULL AND extra IS NOT NULL;
Aqui está uma demonstração:http://www.sqlfiddle.com/#!4/ aacc3/1
--- EDITAR ----
Eu não percebi, que na saída esperada deptno 10 foi atualizado para 20,
para atualizar
deptno
uma outra consulta é necessária:UPDATE emp
SET deptno = 20
WHERE deptno = 10;
---- EDITAR -----
Se você deseja inserir valores alterados na outra tabela, tente um procedimento com 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;
/
O procedimento deve funcionar se você não for processar um grande número de registros em uma chamada (mais de 1000 ... ou no máximo alguns milhares). Se um
dept_id
pode conter dez mil e mais linhas, então este procedimento pode ser lento, pois consumirá uma enorme quantidade de memória PGA. Nesse caso, uma outra abordagem com coleta em massag em pedaços é necessária.-- EDIT --- como armazenar valores de sequência -------
Presumo que a tabela
changed
tem 4 colunas, assim: CREATE TABLE "TEST"."CHANGED"
( "DEPTNO" NUMBER,
"OLDVAL" NUMBER,
"NEWVAL" NUMBER,
"SEQ_NEXTVAL" NUMBER
) ;
e armazenaremos valores de sequência no
seq_nextval
column.Nesse caso, o procedimento pode ficar assim:
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 --- versão com cursor para pequenos conjuntos de dados -----
Sim, para pequenos conjuntos de dados, a coleta em massa não aumenta significativamente a velocidade, e o cursor simples com for..loop é suficiente nesse caso.
Abaixo está um exemplo como você usa o cursor junto com atualização, observe o
FOR UPDATE
cláusula, ela é necessária quando planejamos atualizar um registro obtido do cursor usando WHERE CURRENT OF
cláusula.Desta vez, um valor de sequência é avaliado dentro da instrução 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;