【问题标题】:update using for loop in plsql在plsql中使用for循环更新
【发布时间】:2013-09-07 06:14:42
【问题描述】:

我在更新和插入下面的列时遇到问题。请就此提出建议。

这是输入

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

我需要在以下条件下使用extra 列更新comm 列。

  • 如果 comm 为 null,则将额外值更新为 comm。
  • 如果 comm 不为空,则无需更新,
  • 如果两者都为空,则保留为空,
  • 如果 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;

请就上述内容发表意见。它没有正确插入。

【问题讨论】:

    标签: oracle plsqldeveloper


    【解决方案1】:

    您不需要FOR LOOP,只需一个更新即可完成工作:

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

    这是一个演示:http://www.sqlfiddle.com/#!4/aacc3/1

    --- 编辑 ----

    我没有注意到,在预期的输出中,deptno 10 已更新为 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;
    /
    

    如果您不打算在一次调用中处理大量记录(超过 1000 条......或最多几千条),该过程应该可以工作。如果一个dept_id可以包含一万多行,那么这个过程可能会很慢,因为它会消耗大量的PGA内存。在这种情况下,需要另一种以块形式进行批量收集的方法。

    -- EDIT --- 如何存储序列值 -------

    我假设表 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;
    



    --- EDIT --- 带有光标的小数据集版本 -----

    是的,对于小型数据集,批量收集不会显着提高速度,在这种情况下,带有 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;
    

    【讨论】:

    • 对不起,我刚刚编辑过,现在只有 deptno 20,但是如果更新了许多 comm= null,如何插入.. 正如我在插入 changed_comm 的代码中提到的,仅在单个过程中完成.请帮我解决这个问题
    • 对不起,我刚刚编辑过,现在只有 deptno 20,但是如果更新了许多 comm= null,如何插入.. 正如我在插入 changed_comm 的代码中提到的,仅在单个过程中完成.请帮我解决这个问题
    • @user2686661 我改变了答案,有一个可能有帮助的过程示例。
    • 感谢你,用那个例子试试
    • @user2686661 我更正了示例,上一个在更新后插入了值,我的错误,我很抱歉,看看更正的版本。
    【解决方案2】:
     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;  
    

    【讨论】:

    • 欢迎来到 SO。您的答案是通过添加代码标签来编辑的,以便于阅读。对于这种情况,解释您的答案如何成为已接受答案的良好替代方案将是一种很好的做法。另外,您可能需要查看链接:stackoverflow.com/help/how-to-answer
    猜你喜欢
    • 2021-03-02
    • 1970-01-01
    • 1970-01-01
    • 2016-10-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-17
    • 1970-01-01
    相关资源
    最近更新 更多