web-dev-qa-db-ja.com

plsqlのforループを使用して更新

更新に問題があり、下の列に挿入します。これについてアドバイスしてください。

これは入力です

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列に更新する必要があります。

  • Commがnullの場合、追加の値はcommに更新されます。
  • Commがnullでない場合、更新する必要はありません。
  • 両方がnullの場合は、nullのままにします。
  • comm列に値がある場合、上書きする必要はありません。

私のプログラムは以下の通りです。どの行が更新され、どの値が別のテーブルにあるかを追跡する必要があります。

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;

上記についてご意見をお聞かせください。正しく挿入されていません。

6
user2686661

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;
10
krokodilko
 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;