更新に問題があり、下の列に挿入します。これについてアドバイスしてください。
これは入力です
depnto extra comm
----------------------------
20 300 NULL
20 300 400
20 NULL NULL
20 500 NULL
これは期待される出力です
depnto Extra comm
---------------------
20 300 300
20 300 400
20 NULL NULL
20 500 500
以下の条件で、comm
列をextra
列に更新する必要があります。
私のプログラムは以下の通りです。どの行が更新され、どの値が別のテーブルにあるかを追跡する必要があります。
PROCEDURE (dept_id )
AS
BEGIN
FOR r IN (SELECT *
FROM emp
WHERE comm IS NULL AND extra IS NOT NULL AND deptno = dept_id)
LOOP
UPDATE emp
SET comm = extra
WHERE comm IS NULL AND extra IS NOT NULL AND deptno = dept_id;
INSERT INTO changed_comm (deptno, oldval, newval)
VALUES (dept_id, r.comm, r.extra);
END LOOP;
EXCEPTION
WHEN NO_DATA_FOUND
THEN
NULL;
END;
上記についてご意見をお聞かせください。正しく挿入されていません。
FOR LOOP
は必要ありません。1回のUPDATEで機能します。
UPDATE emp
SET comm = extra
WHERE comm IS NULL AND extra IS NOT NULL;
これがデモです: http://www.sqlfiddle.com/#!4/aacc3/1
---編集----
期待される出力でdeptno10が20に更新されたことに気づきませんでした。deptno
を更新するには、別のクエリが必要です。
UPDATE emp
SET deptno = 20
WHERE deptno = 10;
----編集-----
変更された値を他のテーブルに挿入する場合は、RETURNING..BULKCOLLECTおよび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;
/
1回の呼び出しで膨大な数のレコード(1000を超える...または最大で数千)を処理しない場合は、この手順が機能するはずです。 1つのdept_id
に1万以上の行を含めることができる場合、大量のPGAメモリを消費するため、この手順は遅くなる可能性があります。このような場合、一括収集をチャンクで行う別のアプローチが必要です。
-編集---シーケンス値の保存方法-------
テーブルchanged
には、次のように4つの列があると想定しています。
CREATE TABLE "TEST"."CHANGED"
( "DEPTNO" NUMBER,
"OLDVAL" NUMBER,
"NEWVAL" NUMBER,
"SEQ_NEXTVAL" NUMBER
) ;
シーケンス値をseq_nextval
列に格納します。
このような場合、手順は次のようになります。
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;
---編集---小さなデータセット用のカーソル付きバージョン-----
はい、データの小さなセットの場合、一括収集では速度が大幅に向上することはありません。そのような場合は、for..loopを使用したプレーンカーソルで十分です。
以下は、tuがカーソルを更新と一緒に使用する方法の例です。FOR UPDATE
句に注意してください。WHERE CURRENT OF
句を使用してカーソルからフェッチされたレコードを更新する場合に必要です。
今回は、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;
BEGIN
FOR person IN (SELECT A FROM EMP WHERE B IN (SELECT B FROM ustom.cfd_180518) )
LOOP
--dbms_output.put_line(person.A);
UPDATE custom.cfd_180518 SET c = person.a;
END LOOP;
END;