Oracle
 sql >> Datenbank >  >> RDS >> Oracle

Update mit for-Schleife in plsql

Sie brauchen FOR LOOP nicht , nur ein einziges UPDATE erledigt die Arbeit:

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

Hier ist eine Demo:http://www.sqlfiddle.com/#!4/ aacc3/1

--- BEARBEITEN ----

Ich habe nicht bemerkt, dass in der erwarteten Ausgabe deptno 10 auf 20 aktualisiert wurde,
um deptno zu aktualisieren eine weitere Abfrage wird benötigt:

UPDATE emp
   SET deptno = 20
WHERE deptno = 10;



---- BEARBEITEN -----

Wenn Sie geänderte Werte in die andere Tabelle einfügen möchten, versuchen Sie es mit RETURNING..BULK COLLECT und 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;
/

Das Verfahren sollte funktionieren, wenn Sie keine große Anzahl von Datensätzen in einem Aufruf verarbeiten möchten (mehr als 1000 ... oder maximal einige Tausend). Wenn eine dept_id Zehntausende und mehr Zeilen enthalten kann, kann diese Prozedur langsam sein, da sie eine große Menge an PGA-Speicher verbraucht. In einem solchen Fall ist ein anderer Ansatz mit Massensammlung in Blöcken erforderlich.

-- BEARBEITEN --- wie man Sequenzwerte speichert -------

Ich nehme an, dass sich die Tabelle changed hat hat 4 Spalten, etwa so:

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

und wir speichern Sequenzwerte in seq_nextval Spalte.

In einem solchen Fall könnte die Vorgehensweise wie folgt aussehen:

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 --- Version mit Cursor für kleine Datensätze -----

Ja, bei kleinen Datensätzen führt das Massensammeln nicht zu einer signifikanten Geschwindigkeitssteigerung, und in einem solchen Fall ist ein einfacher Cursor mit for..loop ausreichend.
Unten ist ein Beispiel Wie Sie den Cursor zusammen mit Update verwenden, beachten Sie den FOR UPDATE -Klausel, ist sie erforderlich, wenn wir planen, einen vom Cursor abgerufenen Datensatz mit WHERE CURRENT OF zu aktualisieren -Klausel.
Dieses Mal wird ein Sequenzwert innerhalb der INSERT-Anweisung ausgewertet.

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;