sql >> Database >  >> RDS >> Oracle

update met for-lus in plsql

Je hebt FOR LOOP niet nodig , slechts een enkele UPDATE doet het werk:

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

Hier is een demo:http://www.sqlfiddle.com/#!4/ aacc3/1

--- BEWERKEN ----

Ik heb niet gemerkt dat in de verwachte uitvoer deptno 10 is bijgewerkt naar 20,
om deptno bij te werken een andere vraag is nodig:

UPDATE emp
   SET deptno = 20
WHERE deptno = 10;



---- BEWERKEN -----

Als u gewijzigde waarden in de andere tabel wilt invoegen, probeer dan een procedure met RETURNING..BULK COLLECT en 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;
/

De procedure zou moeten werken als u geen enorm aantal records in één oproep gaat verwerken (meer dan 1000 ... of maximaal enkele duizenden). Als één dept_id tienduizenden en meer rijen kan bevatten, kan deze procedure traag zijn, omdat het een enorme hoeveelheid PGA-geheugen zal verbruiken. In zo'n geval is een andere benadering met bulkverzameling in chunks vereist.

-- EDIT --- hoe sequentiewaarden op te slaan -------

Ik neem aan dat de tabel changed heeft 4 kolommen, zoals deze:

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

en we zullen reekswaarden opslaan in de seq_nextval kolom.

In zo'n geval kan de procedure er als volgt uitzien:

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 --- versie met cursor voor kleine datasets -----

Ja, voor kleine datasets geeft het verzamelen van gegevens geen significante verhoging van de snelheid, en een gewone cursor met for..loop is in zo'n geval voldoende.
Hieronder staat een voorbeeld hoe u de cursor samen met update gebruikt, let op de FOR UPDATE clausule, is het vereist wanneer we van plan zijn een record bij te werken dat van de cursor is opgehaald met WHERE CURRENT OF clausule.
Deze keer wordt een reekswaarde geëvalueerd binnen de INSERT-instructie.

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;


  1. Hoe tonen forums u ongelezen onderwerpen?

  2. Hoe ontsnap ik aan een percentageteken in T-SQL?

  3. SQL Query - Verwijder duplicaten als er meer dan 3 dups zijn?

  4. Hoe een join-query te gebruiken voor 4 tabellen in rails