Nie potrzebujesz FOR LOOP
, wystarczy jedna aktualizacja:
UPDATE emp
SET comm = extra
WHERE comm IS NULL AND extra IS NOT NULL;
Oto demo:http://www.sqlfiddle.com/#!4/ aacc3/1
--- EDYTUJ ----
Nie zauważyłem, że w oczekiwanym wyniku deptno 10 zostało zaktualizowane do 20,
aby zaktualizować deptno
potrzebne jest inne zapytanie:
UPDATE emp
SET deptno = 20
WHERE deptno = 10;
---- EDYTUJ -----
Jeśli chcesz wstawić zmienione wartości do innej tabeli, wypróbuj procedurę z RETURNING..BULK COLLECT i 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;
/
Procedura powinna zadziałać, jeśli nie zamierzasz przetwarzać ogromnej liczby rekordów w jednym połączeniu (ponad 1000...lub maksymalnie kilka tysięcy). Jeśli jeden dept_id
może zawierać dziesięć tysięcy i więcej wierszy, wtedy ta procedura może być powolna, ponieważ zużyje ogromną ilość pamięci PGA. W takim przypadku wymagane jest inne podejście z zbiorczym gromadzeniem w porcjach.
-- EDYCJA --- jak przechowywać wartości sekwencji -------
Zakładam, że tabela changed
ma 4 kolumny, tak:
CREATE TABLE "TEST"."CHANGED"
( "DEPTNO" NUMBER,
"OLDVAL" NUMBER,
"NEWVAL" NUMBER,
"SEQ_NEXTVAL" NUMBER
) ;
i będziemy przechowywać wartości sekwencji w seq_nextval
kolumna.
W takim przypadku procedura może wyglądać tak:
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;
--- EDYCJA --- wersja z kursorem dla małych zestawów danych -----
Tak, dla małych zbiorów danych masowe zbieranie nie daje znaczącego wzrostu szybkości, a zwykły kursor z for..loop jest w takim przypadku wystarczający.
Poniżej przykład jak używać kursora razem z aktualizacją, zwróć uwagę na FOR UPDATE
klauzula, jest wymagana, gdy planujemy zaktualizować rekord pobrany z kursora za pomocą WHERE CURRENT OF
klauzula.
Tym razem wartość sekwencji jest oceniana w instrukcji 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;