7

i'm having problem updating and insert into below column. Please advise on this.

This is the input

depnto   extra    comm
----------------------------
20       300      NULL
20       300      400
20       NULL     NULL
20       500      NULL

This is the expected output

depnto  Extra    comm
---------------------
20      300      300
20      300      400
20      NULL     NULL           
20      500      500

I need to update comm column with extra column on below conditions.

  • If comm Is null then extra value is updated to comm.
  • If comm Is not null, no need to update,
  • If both are null, leave as null,
  • if comm column has a value no need to overwrite.

My program is below. Even I need to keep track which are rows are updated and to which value in another table.

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;

please provide some opinion on above. Its not inserting correctly.

user2686661
  • 95
  • 1
  • 2
  • 9

2 Answers2

10

You do not need FOR LOOP, just a single UPDATE does the work:

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

Here is a demo: http://www.sqlfiddle.com/#!4/aacc3/1

--- EDIT ----

I didn't notice, that in the expected output deptno 10 was updated to 20,
to update deptno an another query is needed:

UPDATE emp
   SET deptno = 20
WHERE deptno = 10;



---- EDIT -----

If you want to insert changed values to the other table, try a procedure with RETURNING..BULK COLLECT and 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;
/

The procedure should work if you are not going to process huge number of records in a one call (more than 1000 ... or maximum a few thousands). If one dept_id can contain ten thousands and more rows, then this procedure might be slow, becasue it will consume a huge amount of PGA memory. In such a case, an another approach with bulk collectiong in chunks is required.

-- EDIT --- how to store sequence values -------

I assume that the table changed has 4 columns, like this:

  CREATE TABLE "TEST"."CHANGED" 
   (    "DEPTNO" NUMBER, 
        "OLDVAL" NUMBER, 
        "NEWVAL" NUMBER, 
        "SEQ_NEXTVAL" NUMBER 
   ) ;

and we will store sequence values in the seq_nextval column.

In such a case the procedure might look like this:

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 --- version with cursor for small sets of data -----

Yes, for small sets of data bulk collecting doesn't give significant increase of the speed, and plain cursor with for..loop is sufficient in such a case.
Below is an example how tu use the cursor together with update, notice the FOR UPDATE clause, it is required when we plan to update a record fetched from the cursor using WHERE CURRENT OF clause.
This time a sequence value is evaluated within the INSERT statement.

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;
krokodilko
  • 35,300
  • 7
  • 55
  • 79
  • Sorry i just edited , now there is only deptno 20, but how to insert if many comm= null gets updated.. as i mentioned in code to insert into changed_comm , to be done in single procedure only. Please help me on this – user2686661 Sep 07 '13 at 11:10
  • Sorry i just edited , now there is only deptno 20, but how to insert if many comm= null gets updated.. as i mentioned in code to insert into changed_comm , to be done in single procedure only. Please help me on this – user2686661 Sep 07 '13 at 11:12
  • @user2686661 I changed the answer, there is an example of the procedure that might help. – krokodilko Sep 07 '13 at 14:24
  • Grateful to you, Will try with that example – user2686661 Sep 07 '13 at 14:33
  • @user2686661 I corrected the example, the previous one inserted values AFTER update, My mistake, I apologize, take a look at corrected version. – krokodilko Sep 07 '13 at 14:49
  • the procedure is giving same value for old and new. i need old value as null new value as of updated one. another one question Sir what if that im having another column to insert as sequence like sequence.nextval. – user2686661 Sep 07 '13 at 15:59
  • its working , another question on insert if i specify column like insert into (new_val,old_val dept_no, sequence.nextval) values.. then how to changed_buff( i ). please advice im new to this BULK, FORALL. – user2686661 Sep 08 '13 at 01:21
  • @user2686661, I changed the answer and appended an example how to store sequence values in the table. – krokodilko Sep 08 '13 at 10:48
  • Thank YOU its working. if i want to update with small set of data can we use cursor? – user2686661 Sep 08 '13 at 16:03
  • I appended another example using cursor, best regards. – krokodilko Sep 08 '13 at 21:45
3
 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;  
Hakan ERDOGAN
  • 1,150
  • 8
  • 19
  • Welcome to SO. Your answer is edited by adding code tags for easy readability. Explaining how your answer is a good alternative to the accepted answer would be a good practice for such cases. Also, you might want to check the link: https://stackoverflow.com/help/how-to-answer – Hakan ERDOGAN May 19 '18 at 09:17