Oracle
 sql >> Database >  >> RDS >> Oracle

aggiorna usando for loop in plsql

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;