Oracle
 sql >> Base de Dados >  >> RDS >> Oracle

atualizar usando for loop no plsql


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;