6

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

这是输入

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,
  • 如果 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;

请提供一些意见以上。它没有正确插入。

4

2 回答 2

10

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

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 内存。在这种情况下,需要另一种批量收集块的方法。

-- 编辑 --- 如何存储序列值 -------

我假设表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子句,当我们计划更新从游标 usingWHERE 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;
于 2013-09-07T09:28:56.250 回答
1
 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;  
于 2018-05-19T08:52:49.663 回答