sql >> データベース >  >> RDS >> Oracle

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

    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..BULK COLLECTおよび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を使用したプレーンカーソルで十分です。
    以下に例を示します。更新と一緒にカーソルを使用する方法については、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;
    


    1. PostgreSQLでのテキスト圧縮

    2. 単一の列を持つテーブルから最初のNを除くすべての行を削除します

    3. Oracle:行タイプデータを別のテーブルに挿入します

    4. SlonyスレーブノードでDELETE/UPDATEではなくINSERTのみを複製する方法は?