sql >> Base de Datos >  >> RDS >> Oracle

actualizar usando for loop en plsql

No necesitas FOR LOOP , solo una ACTUALIZACIÓN hace el trabajo:

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

Aquí hay una demostración:http://www.sqlfiddle.com/#!4/ aacc3/1

--- EDITAR ----

No me di cuenta de que en la salida esperada, el departamento 10 se actualizó a 20,
para actualizar deptno se necesita otra consulta:

UPDATE emp
   SET deptno = 20
WHERE deptno = 10;



---- EDITAR -----

Si desea insertar valores modificados en la otra tabla, pruebe un procedimiento con RETURNING...BULK COLLECT y 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;
/

El procedimiento debería funcionar si no va a procesar una gran cantidad de registros en una sola llamada (más de 1000... o un máximo de unos pocos miles). Si uno dept_id puede contener diez mil y más filas, entonces este procedimiento puede ser lento, ya que consumirá una gran cantidad de memoria PGA. En tal caso, se requiere otro enfoque con recolección masiva en fragmentos.

-- EDITAR --- cómo almacenar valores de secuencia -------

Supongo que la tabla changed tiene 4 columnas, así:

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

y almacenaremos valores de secuencia en el seq_nextval columna.

En tal caso, el procedimiento podría verse así:

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;



--- EDITAR --- versión con cursor para pequeños conjuntos de datos -----

Sí, para pequeños conjuntos de datos, la recopilación masiva no proporciona un aumento significativo de la velocidad, y el cursor simple con for..loop es suficiente en tal caso.
A continuación se muestra un ejemplo cómo usar el cursor junto con la actualización, observe el FOR UPDATE cláusula, se requiere cuando planeamos actualizar un registro obtenido del cursor usando WHERE CURRENT OF cláusula.
Esta vez se evalúa un valor de secuencia dentro de la instrucción 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;