Home » SQL & PL/SQL » SQL & PL/SQL » PL/SQL for Insert/Update
PL/SQL for Insert/Update [message #633181] Thu, 12 February 2015 01:42 Go to next message
aimy
Messages: 225
Registered: June 2006
Senior Member
Hi.

My PL/SQL code is this
CREATE OR REPLACE procedure P_S13018_TEST as
--===================================================================================--

/*
Thursday, 12 February, 2015 11:51:42 AM - Sim Chun Seng method
*/

TYPE tbl IS TABLE OF CSM_DAILY_CKC%ROWTYPE;
src tbl;
row_update number(10):=0;
row_insert number(10):=0;
row_reject number(10):=0;
i number:=0;
j number:=0;
v_ErrorCode number;
v_Errortext varchar2(200);
start_time date := sysdate;
proc_name VARCHAR2(36) := 'P_S13018_TEST';
table_name VARCHAR2(36) := 'S13018_CSM_DAILY_CKC';
subject_area VARCHAR2(45) := 'CSM-CKC Daily Report';

begin

    select * BULK COLLECT into src
    from CSM_DAILY_CKC where system_date='13/12/2014';

      FOR i IN src.first .. src.last LOOP
        UPDATE S13018_CSM_DAILY_CKC SET
          CKC_ID = src(i).CKC_ID,
          SR_NUM = src(i).SR_NUM,
          TR_NUM = src(i).TR_NUM,
          CKC_STATUS = src(i).CKC_STATUS,
          CKC_CREATED_DATE = src(i).CKC_CREATED_DATE,
          SYSTEM_DATE = src(i).SYSTEM_DATE,
          AGING = src(i).AGING,
          CUSTOMER_NAME = src(i).CUSTOMER_NAME,
          SERVICE_NUM = src(i).SERVICE_NUM,
          LOGIN_ID = src(i).LOGIN_ID,
          SIEBEL_CUST_ID = src(i).SIEBEL_CUST_ID,
          PRIMARY_CONTACT = src(i).PRIMARY_CONTACT,
          MAIN_CONTACT_MOBILE = src(i).MAIN_CONTACT_MOBILE,
          MAIN_CONTACT_HOME = src(i).MAIN_CONTACT_HOME,
          MAIN_CONTACT_OFFICE = src(i).MAIN_CONTACT_OFFICE,
          MAIN_CONTACT_OFFICE_EXT = src(i).MAIN_CONTACT_OFFICE_EXT,
          BUILDING_ID = src(i).BUILDING_ID,
          CABINET_ID = src(i).CABINET_ID,
          DP_ID = src(i).DP_ID,
          DSLAM_ID = src(i).DSLAM_ID,
          DSLAM_CARD_SLOT = src(i).DSLAM_CARD_SLOT,
          CABLE_ID = src(i).CABLE_ID,
          DLC_CARD_SLOT = src(i).DLC_CARD_SLOT,
          VBBS_CODE = src(i).VBBS_CODE,
          ZONE_NAME = src(i).ZONE_NAME,
          STATE = src(i).STATE,
          CKC_DIAGNOSIS_CODE = src(i).CKC_DIAGNOSIS_CODE,
          CKC_OWNER_POSITION = src(i).CKC_OWNER_POSITION,
          CKC_CREATOR_GROUP = src(i).CKC_CREATOR_GROUP,
          CKC_CREATOR_POSITION = src(i).CKC_CREATOR_POSITION,
          MANUFACTURER_NUM = src(i).MANUFACTURER_NUM,
          LEVEL_IND = src(i).LEVEL_IND
        WHERE
          CKC_ID = src(i).CKC_ID and
          SIEBEL_CUST_ID = src(i).SIEBEL_CUST_ID and
          CUSTOMER_NAME = src(i).CUSTOMER_NAME and
          SERVICE_NUM = src(i).SERVICE_NUM
          ;

      row_update := row_update+1;

        IF SQL%ROWCOUNT = 0 THEN
          INSERT INTO S13018_CSM_DAILY_CKC (
          CKC_ID, SR_NUM, TR_NUM, CKC_STATUS, CKC_CREATED_DATE, SYSTEM_DATE, AGING, CUSTOMER_NAME, SERVICE_NUM, LOGIN_ID, SIEBEL_CUST_ID,
          PRIMARY_CONTACT, MAIN_CONTACT_MOBILE, MAIN_CONTACT_HOME, MAIN_CONTACT_OFFICE, MAIN_CONTACT_OFFICE_EXT, BUILDING_ID, CABINET_ID,
          DP_ID, DSLAM_ID, DSLAM_CARD_SLOT, CABLE_ID, DLC_CARD_SLOT, VBBS_CODE, ZONE_NAME, STATE, CKC_DIAGNOSIS_CODE, CKC_OWNER_POSITION,
          CKC_CREATOR_GROUP, CKC_CREATOR_POSITION, MANUFACTURER_NUM, LEVEL_IND
          )
          VALUES (
          src(i).CKC_ID, src(i).SR_NUM, src(i).TR_NUM, src(i).CKC_STATUS, src(i).CKC_CREATED_DATE, src(i).SYSTEM_DATE, src(i).AGING,
          src(i).CUSTOMER_NAME, src(i).SERVICE_NUM, src(i).LOGIN_ID, src(i).SIEBEL_CUST_ID, src(i).PRIMARY_CONTACT,
          src(i).MAIN_CONTACT_MOBILE, src(i).MAIN_CONTACT_HOME, src(i).MAIN_CONTACT_OFFICE, src(i).MAIN_CONTACT_OFFICE_EXT,
          src(i).BUILDING_ID, src(i).CABINET_ID, src(i).DP_ID, src(i).DSLAM_ID, src(i).DSLAM_CARD_SLOT, src(i).CABLE_ID, src(i).DLC_CARD_SLOT,
          src(i).VBBS_CODE, src(i).ZONE_NAME, src(i).STATE, src(i).CKC_DIAGNOSIS_CODE, src(i).CKC_OWNER_POSITION, src(i).CKC_CREATOR_GROUP,
          src(i).CKC_CREATOR_POSITION, src(i).MANUFACTURER_NUM, src(i).LEVEL_IND
          );

        row_insert := row_insert +1;

        END IF;

      END LOOP;

--      LOG ERRORS INTO ('daily_load') REJECT LIMIT 25;
      commit;

        dbms_output.put_line(chr(0));
        dbms_output.put_line(chr(10));
        dbms_output.put_line('UPDATE for '||table_name);
        dbms_output.put_line('-------------------------------------------------');
        dbms_output.put_line(chr(0));
        DBMS_OUTPUT.PUT_LINE (row_update ||' rows have been updated!');
        DBMS_OUTPUT.PUT_LINE (row_insert ||' rows have been inserted!');
        DBMS_OUTPUT.PUT_LINE (row_reject ||' rows have been rejected!');
        DBMS_OUTPUT.PUT_LINE('error_code:'||v_ErrorCode);
        DBMS_OUTPUT.PUT_LINE('error_msg:'||v_ErrorText);
        dbms_output.put_line(chr(0));

     insert into PROCEDURE_TRACKING_LOG
     (procedure_name, table_name, rows_inserted, rows_updated, rows_deleted, rows_inserted_of_deletion, rows_inserted_after_deletion,
      rows_rejected, start_time, end_time, elapse_time, remarks, subject_area)
     values
     (proc_name,
   table_name,row_insert,row_update,NULL,NULL,NULL,row_reject,start_time,sysdate,(sysdate-start_time)* 86400,
      v_ErrorText,subject_area);

commit;

END;
/


My question is how do I capture the rows_updated and rows_inserted correctly?

It seems that the row_update give the total count of the source table.

Thank you.
Re: PL/SQL for Insert/Update [message #633184 is a reply to message #633181] Thu, 12 February 2015 01:58 Go to previous messageGo to next message
Michel Cadot
Messages: 68776
Registered: March 2007
Location: Saint-Maur, France, https...
Senior Member
Account Moderator

Row by row is slow by slow.
Change the whole code.

Re: PL/SQL for Insert/Update [message #633188 is a reply to message #633184] Thu, 12 February 2015 02:16 Go to previous messageGo to next message
aimy
Messages: 225
Registered: June 2006
Senior Member
Michel Cadot wrote on Thu, 12 February 2015 15:58

Row by row is slow by slow.
Change the whole code.


Thanks for your feedback.

But what do you actually mean?

By the way, I used to use this method, but one of my colleague says this..
dwh job should be performed in bulks rather than row by row.
example below is many times simpler and standard... 

BEGIN
 INSERT INTO s53449.test1
    SELECT account_num
        FROM tel_profile
        WHERE rownum < 100
    LOG ERRORS INTO s53449.err ('daily_load') REJECT LIMIT 25;
    commit;
      DBMS_OUTPUT.PUT_LINE('record inserted: ' || TO_CHAR(SQL%ROWCOUNT));
END;
/


My common code:
CREATE OR REPLACE procedure P_S13018_TEST as
--===================================================================================--

cursor A is
select* from CSM_DAILY_CKC where system_date='13/12/2014';

arec A%rowtype;

row_update number(10):=0;
row_insert number(10):=0;
row_reject number(10):=0;
i number:=0;
j number:=0;
v_ErrorCode number;
v_Errortext varchar2(200);
start_time date := sysdate;
proc_name VARCHAR2(36) := 'P_S13018_TEST';
table_name VARCHAR2(36) := 'S13018_CSM_DAILY_CKC';
subject_area VARCHAR2(45) := 'CSM-CKC Daily Report';

begin

     OPEN  A;

     loop
     FETCH A INTO arec;
     exit when A%notfound;

     BEGIN


        UPDATE S13018_CSM_DAILY_CKC SET
          CKC_ID = arec.CKC_ID,
          SR_NUM = arec.SR_NUM,
          TR_NUM = arec.TR_NUM,
          CKC_STATUS = arec.CKC_STATUS,
          CKC_CREATED_DATE = arec.CKC_CREATED_DATE,
          SYSTEM_DATE = arec.SYSTEM_DATE,
          AGING = arec.AGING,
          CUSTOMER_NAME = arec.CUSTOMER_NAME,
          SERVICE_NUM = arec.SERVICE_NUM,
          LOGIN_ID = arec.LOGIN_ID,
          SIEBEL_CUST_ID = arec.SIEBEL_CUST_ID,
          PRIMARY_CONTACT = arec.PRIMARY_CONTACT,
          MAIN_CONTACT_MOBILE = arec.MAIN_CONTACT_MOBILE,
          MAIN_CONTACT_HOME = arec.MAIN_CONTACT_HOME,
          MAIN_CONTACT_OFFICE = arec.MAIN_CONTACT_OFFICE,
          MAIN_CONTACT_OFFICE_EXT = arec.MAIN_CONTACT_OFFICE_EXT,
          BUILDING_ID = arec.BUILDING_ID,
          CABINET_ID = arec.CABINET_ID,
          DP_ID = arec.DP_ID,
          DSLAM_ID = arec.DSLAM_ID,
          DSLAM_CARD_SLOT = arec.DSLAM_CARD_SLOT,
          CABLE_ID = arec.CABLE_ID,
          DLC_CARD_SLOT = arec.DLC_CARD_SLOT,
          VBBS_CODE = arec.VBBS_CODE,
          ZONE_NAME = arec.ZONE_NAME,
          STATE = arec.STATE,
          CKC_DIAGNOSIS_CODE = arec.CKC_DIAGNOSIS_CODE,
          CKC_OWNER_POSITION = arec.CKC_OWNER_POSITION,
          CKC_CREATOR_GROUP = arec.CKC_CREATOR_GROUP,
          CKC_CREATOR_POSITION = arec.CKC_CREATOR_POSITION,
          MANUFACTURER_NUM = arec.MANUFACTURER_NUM,
          LEVEL_IND = arec.LEVEL_IND
        WHERE
          CKC_ID = arec.CKC_ID and
          SIEBEL_CUST_ID = arec.SIEBEL_CUST_ID and
          CUSTOMER_NAME = arec.CUSTOMER_NAME and
          SERVICE_NUM = arec.SERVICE_NUM
          ;

     IF SQL%FOUND then
     row_update := row_update+1;
    else
    
       INSERT INTO CSM_DAILY_CKC
       (CKC_ID, SR_NUM, TR_NUM, CKC_STATUS, CKC_CREATED_DATE, SYSTEM_DATE, AGING, CUSTOMER_NAME, SERVICE_NUM, LOGIN_ID, SIEBEL_CUST_ID,
        PRIMARY_CONTACT, MAIN_CONTACT_MOBILE, MAIN_CONTACT_HOME, MAIN_CONTACT_OFFICE, MAIN_CONTACT_OFFICE_EXT, BUILDING_ID, CABINET_ID,
        DP_ID, DSLAM_ID, DSLAM_CARD_SLOT, CABLE_ID, DLC_CARD_SLOT, VBBS_CODE, ZONE_NAME, STATE)
       VALUES
       (arec.CKC_ID, arec.SR_NUM, arec.TR_NUM, arec.CKC_STATUS, arec.CKC_CREATED_DATE, arec.SYSTEM_DATE, arec.AGING, arec.CUSTOMER_NAME,
        arec.SERVICE_NUM, arec.LOGIN_ID, arec.SIEBEL_CUST_ID, arec.PRIMARY_CONTACT, arec.MAIN_CONTACT_MOBILE, arec.MAIN_CONTACT_HOME,
        arec.MAIN_CONTACT_OFFICE, arec.MAIN_CONTACT_OFFICE_EXT, arec.BUILDING_ID, arec.CABINET_ID, arec.DP_ID, arec.DSLAM_ID,
        arec.DSLAM_CARD_SLOT, arec.CABLE_ID, arec.DLC_CARD_SLOT, arec.VBBS_CODE, arec.ZONE_NAME, arec.STATE)
        ;

     row_insert := row_insert +1;

     i := i + 1;

     if i > 500 then
     commit;
     i := 0;
     end if;

END IF;

     exception when others then
     row_reject:=row_reject+1;
     v_ErrorCode :=SQLCODE;
     v_ErrorText :=SUBSTR(SQLERRM,1,200);


     end;

     i := i + 1;

     if i > 500 then
     commit;
     i := 0;
     end if;

     END LOOP;


     close A;

    dbms_output.put_line(chr(0));
     dbms_output.put_line(chr(10));
     dbms_output.put_line('UPDATE for '||table_name);
     dbms_output.put_line('-------------------------------------------------');
    dbms_output.put_line(chr(0));
    DBMS_OUTPUT.PUT_LINE (row_update ||' rows have been updated!');
     DBMS_OUTPUT.PUT_LINE (row_insert ||' rows have been inserted!');
     DBMS_OUTPUT.PUT_LINE (row_reject ||' rows have been rejected!');
     DBMS_OUTPUT.PUT_LINE('error_code:'||v_ErrorCode);
     DBMS_OUTPUT.PUT_LINE('error_msg:'||v_ErrorText);
     dbms_output.put_line(chr(0));

     insert into PROCEDURE_TRACKING_LOG
     (procedure_name, table_name, rows_inserted, rows_updated, rows_deleted, rows_inserted_of_deletion, rows_inserted_after_deletion,
      rows_rejected, start_time, end_time, elapse_time, remarks, subject_area)
     values
     (proc_name,
   table_name,row_insert,row_update,NULL,NULL,NULL,row_reject,start_time,sysdate,(sysdate-start_time)* 86400,
      v_ErrorText,subject_area);

commit;

END;
/

Re: PL/SQL for Insert/Update [message #633189 is a reply to message #633188] Thu, 12 February 2015 02:20 Go to previous messageGo to next message
Michel Cadot
Messages: 68776
Registered: March 2007
Location: Saint-Maur, France, https...
Senior Member
Account Moderator

Quote:
By the way, I used to use this method, but one of my colleague says this..


And your colleague is right.

Quote:
My common code:


It is wrong.

Re: PL/SQL for Insert/Update [message #633190 is a reply to message #633189] Thu, 12 February 2015 02:29 Go to previous messageGo to next message
aimy
Messages: 225
Registered: June 2006
Senior Member
Why it is wrong? Sad

Can you lend me a help?

Thank you.
Re: PL/SQL for Insert/Update [message #633191 is a reply to message #633190] Thu, 12 February 2015 02:36 Go to previous messageGo to next message
Michel Cadot
Messages: 68776
Registered: March 2007
Location: Saint-Maur, France, https...
Senior Member
Account Moderator

Row by row (for loop) is slow by slow.
Use BULK operation as your colleague told you.

Re: PL/SQL for Insert/Update [message #633192 is a reply to message #633190] Thu, 12 February 2015 02:38 Go to previous messageGo to next message
Lalit Kumar B
Messages: 3174
Registered: May 2013
Location: World Wide on the Web
Senior Member
aimy wrote on Thu, 12 February 2015 13:59
Why it is wrong?


It's actually not wrong, but inefficient.

Oracle uses two engines to process PL/SQL code. PL/SQL code is handled by the PL/SQL engine while all SQL is handled by the SQL engine. There is an overhead associated with each context switch between the two engines. If PL/SQL code loops through a collection performing the same DML operation for each item in the collection it is possible to reduce context switches by bulk binding the whole collection to the DML statement in one operation. Read this nice article by Tim. Do the test yourself and understand.
Re: PL/SQL for Insert/Update [message #633193 is a reply to message #633191] Thu, 12 February 2015 02:39 Go to previous messageGo to next message
aimy
Messages: 225
Registered: June 2006
Senior Member
Michel Cadot wrote on Thu, 12 February 2015 16:36

Row by row (for loop) is slow by slow.
Use BULK operation as your colleague told you.


Is it the same as BULK COLLECT in the first post?

If I use this method..
BEGIN
 INSERT INTO s53449.test1
    SELECT account_num
        FROM tel_profile
        WHERE rownum < 100
    LOG ERRORS INTO s53449.err ('daily_load') REJECT LIMIT 25;
    commit;
      DBMS_OUTPUT.PUT_LINE('record inserted: ' || TO_CHAR(SQL%ROWCOUNT));
END;
/


What should be the code if I want to do the update as well?

Thank you.
Re: PL/SQL for Insert/Update [message #633194 is a reply to message #633193] Thu, 12 February 2015 02:41 Go to previous messageGo to next message
Lalit Kumar B
Messages: 3174
Registered: May 2013
Location: World Wide on the Web
Senior Member
aimy wrote on Thu, 12 February 2015 14:09

What should be the code if I want to do the update as well?


Read the article I provided above.
Re: PL/SQL for Insert/Update [message #633195 is a reply to message #633193] Thu, 12 February 2015 02:46 Go to previous messageGo to next message
Michel Cadot
Messages: 68776
Registered: March 2007
Location: Saint-Maur, France, https...
Senior Member
Account Moderator

Quote:
Is it the same as BULK COLLECT in the first post?


No.
You use a BULK operation to select but you loop to update one row at a time.
See Lalit's post.

Re: PL/SQL for Insert/Update [message #633197 is a reply to message #633194] Thu, 12 February 2015 02:51 Go to previous messageGo to next message
cookiemonster
Messages: 13975
Registered: September 2008
Location: Rainy Manchester
Senior Member
The most efficient way to do it isn't listed in the article - use MERGE

As for why you're update count in the original code is wrong - you're incrementing it before checking if the update did anything.
Re: PL/SQL for Insert/Update [message #633244 is a reply to message #633197] Thu, 12 February 2015 22:13 Go to previous messageGo to next message
aimy
Messages: 225
Registered: June 2006
Senior Member
cookiemonster wrote on Thu, 12 February 2015 16:51
The most efficient way to do it isn't listed in the article - use MERGE

As for why you're update count in the original code is wrong - you're incrementing it before checking if the update did anything.

Thanks for your suggestion.

So I come out with this. Is it OK? It used external package named ETL though.

CREATE OR REPLACE procedure P_S13018_TEST (param varchar2) as
--===================================================================================--

/*
Thursday, 12 February, 2015 11:51:42 AM - Sim Chun Seng method
*/

row_update number(10):=0;
row_insert number(10):=0;
row_reject number(10):=0;
i number:=0;
j number:=0;
v_ErrorCode number;
v_Errortext varchar2(200);
start_time date := sysdate;
proc_name VARCHAR2(36) := 'P_S13018_TEST';
table_name VARCHAR2(36) := 'S13018_CSM_DAILY_CKC';
subject_area VARCHAR2(45) := 'CSM-CKC Daily Report';
custom_text VARCHAR2(36) := param;

begin

    etl.reset_counters;
    MERGE INTO S13018_CSM_DAILY_CKC tgt
    USING (select * from S13018_CSM_DAILY_bkp where ckc_status=param) SRC
    ON  (tgt.CKC_ID = src.CKC_ID and
        tgt.SIEBEL_CUST_ID = src.SIEBEL_CUST_ID and
        tgt.CKC_CREATED_DATE = src.CKC_CREATED_DATE and
        tgt.SERVICE_NUM = src.SERVICE_NUM)
    /* update the data */
    WHEN MATCHED THEN
    UPDATE
    SET
          SR_NUM = CASE ETL.MERGE_COUNTER(ETL.C_UPDATING) when 0 then  SRC.sr_num END,
          TR_NUM = src.TR_NUM,
          CKC_STATUS = src.CKC_STATUS,
          SYSTEM_DATE = src.SYSTEM_DATE,
          AGING = src.AGING,
          CUSTOMER_NAME = src.CUSTOMER_NAME,
          LOGIN_ID = src.LOGIN_ID,
          PRIMARY_CONTACT = src.PRIMARY_CONTACT,
          MAIN_CONTACT_MOBILE = src.MAIN_CONTACT_MOBILE,
          MAIN_CONTACT_HOME = src.MAIN_CONTACT_HOME,
          MAIN_CONTACT_OFFICE = src.MAIN_CONTACT_OFFICE,
          MAIN_CONTACT_OFFICE_EXT = src.MAIN_CONTACT_OFFICE_EXT,
          BUILDING_ID = src.BUILDING_ID,
          CABINET_ID = src.CABINET_ID,
          DP_ID = src.DP_ID,
          DSLAM_ID = src.DSLAM_ID,
          DSLAM_CARD_SLOT = src.DSLAM_CARD_SLOT,
          CABLE_ID = src.CABLE_ID,
          DLC_CARD_SLOT = src.DLC_CARD_SLOT,
          VBBS_CODE = src.VBBS_CODE,
          ZONE_NAME = src.ZONE_NAME,
          STATE = src.STATE,
          CKC_DIAGNOSIS_CODE = src.CKC_DIAGNOSIS_CODE,
          CKC_OWNER_POSITION = src.CKC_OWNER_POSITION,
          CKC_CREATOR_GROUP = src.CKC_CREATOR_GROUP,
          CKC_CREATOR_POSITION = src.CKC_CREATOR_POSITION,
          MANUFACTURER_NUM = src.MANUFACTURER_NUM,
          LEVEL_IND = src.LEVEL_IND,
          timestamp = sysdate
    WHEN NOT MATCHED THEN
          INSERT (
          CKC_ID, SR_NUM, TR_NUM, CKC_STATUS, CKC_CREATED_DATE, SYSTEM_DATE, AGING, CUSTOMER_NAME, SERVICE_NUM, LOGIN_ID, SIEBEL_CUST_ID,
          PRIMARY_CONTACT, MAIN_CONTACT_MOBILE, MAIN_CONTACT_HOME, MAIN_CONTACT_OFFICE, MAIN_CONTACT_OFFICE_EXT, BUILDING_ID, CABINET_ID,
          DP_ID, DSLAM_ID, DSLAM_CARD_SLOT, CABLE_ID, DLC_CARD_SLOT, VBBS_CODE, ZONE_NAME, STATE, CKC_DIAGNOSIS_CODE, CKC_OWNER_POSITION,
          CKC_CREATOR_GROUP, CKC_CREATOR_POSITION, MANUFACTURER_NUM, LEVEL_IND
          )
          VALUES (
          CASE ETL.MERGE_COUNTER(ETL.c_inserting) when 0 then SRC.ckc_id END, src.SR_NUM, src.TR_NUM, src.CKC_STATUS, src.CKC_CREATED_DATE, src.SYSTEM_DATE, src.AGING,
          src.CUSTOMER_NAME, src.SERVICE_NUM, src.LOGIN_ID, src.SIEBEL_CUST_ID, src.PRIMARY_CONTACT,
          src.MAIN_CONTACT_MOBILE, src.MAIN_CONTACT_HOME, src.MAIN_CONTACT_OFFICE, src.MAIN_CONTACT_OFFICE_EXT,
          src.BUILDING_ID, src.CABINET_ID, src.DP_ID, src.DSLAM_ID, src.DSLAM_CARD_SLOT, src.CABLE_ID, src.DLC_CARD_SLOT,
          src.VBBS_CODE, src.ZONE_NAME, src.STATE, src.CKC_DIAGNOSIS_CODE, src.CKC_OWNER_POSITION, src.CKC_CREATOR_GROUP,
          src.CKC_CREATOR_POSITION, src.MANUFACTURER_NUM, src.LEVEL_IND
          );

    row_insert:=etl.get_merge_insert_count;
    row_update:=etl.get_merge_update_count(sql%rowcount);

    commit;

	    insert into PROCEDURE_TRACKING_LOG
	    (procedure_name, table_name, rows_inserted, rows_updated, rows_deleted, rows_inserted_of_deletion, rows_inserted_after_deletion,
	    rows_rejected, start_time, end_time, elapse_time, remarks, subject_area)
	    values
	    (proc_name||replace(decode(custom_text,null,null,' ('||custom_text||')'),' ()'),
      table_name,row_insert,row_update,NULL,NULL,NULL,row_reject,start_time,sysdate,(sysdate-start_time)* 86400,
	    v_ErrorText,subject_area);

      commit;

      dbms_output.put_line(chr(0));
      dbms_output.put_line(chr(10));
      dbms_output.put_line('UPDATE for '||table_name);
      dbms_output.put_line('-------------------------------------------------');
      dbms_output.put_line(chr(0));
      DBMS_OUTPUT.PUT_LINE (row_update ||' rows have been updated!');
      DBMS_OUTPUT.PUT_LINE (row_insert ||' rows have been inserted!');
      DBMS_OUTPUT.PUT_LINE (row_reject ||' rows have been rejected!');
      DBMS_OUTPUT.PUT_LINE('error_code:'||v_ErrorCode);
      DBMS_OUTPUT.PUT_LINE('error_msg:'||v_ErrorText);
      dbms_output.put_line(chr(0));

     exception when others then
     row_reject:=sql%bulk_exceptions.count;
     v_ErrorCode :=SQLCODE;
     v_ErrorText :=SUBSTR(SQLERRM,1,200);
--      LOG ERRORS INTO ('daily_load') REJECT LIMIT 25;
      commit;

END;
/

Re: PL/SQL for Insert/Update [message #633245 is a reply to message #633194] Thu, 12 February 2015 22:15 Go to previous messageGo to next message
aimy
Messages: 225
Registered: June 2006
Senior Member
Lalit Kumar B wrote on Thu, 12 February 2015 16:41
aimy wrote on Thu, 12 February 2015 14:09

What should be the code if I want to do the update as well?


Read the article I provided above.

Thanks.

But too bad today I cannot access the article for reference Sad

So I continue with MERGE method. Furthermore, I have some working sample on that one Smile
Re: PL/SQL for Insert/Update [message #633260 is a reply to message #633245] Fri, 13 February 2015 02:56 Go to previous messageGo to next message
cookiemonster
Messages: 13975
Registered: September 2008
Location: Rainy Manchester
Senior Member
Looks ok, you'll need to test it.
What are these though:
etl.get_merge_insert_count
etl.get_merge_update_count
Re: PL/SQL for Insert/Update [message #633261 is a reply to message #633245] Fri, 13 February 2015 03:12 Go to previous messageGo to next message
Lalit Kumar B
Messages: 3174
Registered: May 2013
Location: World Wide on the Web
Senior Member
aimy wrote on Fri, 13 February 2015 09:45

But too bad today I cannot access the article for reference Sad


The site seemed to be down, now it is up.

Quote:

So I continue with MERGE method. Furthermore, I have some working sample on that one Smile


Merge looks good. But you might not need those DBMS_OUTPUT statements in an ETL process. Once all your testing is over you could remove them.
Re: PL/SQL for Insert/Update [message #633262 is a reply to message #633260] Fri, 13 February 2015 03:13 Go to previous messageGo to next message
Lalit Kumar B
Messages: 3174
Registered: May 2013
Location: World Wide on the Web
Senior Member
cookiemonster wrote on Fri, 13 February 2015 14:26

etl.get_merge_insert_count
etl.get_merge_update_count


Seems like the ETL package used to decompose the SQL%ROWCOUNT for a MERGE statement.
Re: PL/SQL for Insert/Update [message #633263 is a reply to message #633262] Fri, 13 February 2015 03:26 Go to previous messageGo to next message
cookiemonster
Messages: 13975
Registered: September 2008
Location: Rainy Manchester
Senior Member
Thought it was probably something like that, but it didn't occur to me to check for a specific example on the web.
Re: PL/SQL for Insert/Update [message #633265 is a reply to message #633263] Fri, 13 February 2015 03:48 Go to previous messageGo to next message
Lalit Kumar B
Messages: 3174
Registered: May 2013
Location: World Wide on the Web
Senior Member
I saw it for the first time in one of Barbara's post.
Re: PL/SQL for Insert/Update [message #633285 is a reply to message #633265] Fri, 13 February 2015 10:25 Go to previous messageGo to next message
Michel Cadot
Messages: 68776
Registered: March 2007
Location: Saint-Maur, France, https...
Senior Member
Account Moderator

I remember there was a discussion on AskTom about this and the conclusion was something like: given the huge overhead to do this, why do you really need to know the number of updated and inserted rows? why the number merged rows is not sufficient?

Re: PL/SQL for Insert/Update [message #633548 is a reply to message #633181] Sat, 21 February 2015 01:49 Go to previous messageGo to next message
aimy
Messages: 225
Registered: June 2006
Senior Member
This is the ETL package content..
CREATE OR REPLACE PACKAGE CRISPBATCH.ETL AS

   c_inserting CONSTANT PLS_INTEGER := 0;
   c_updating  CONSTANT PLS_INTEGER := 1;

   FUNCTION merge_counter (
            action_in IN PLS_INTEGER DEFAULT c_inserting
            ) RETURN PLS_INTEGER;

   FUNCTION get_merge_update_count RETURN PLS_INTEGER;

   FUNCTION get_merge_update_count (
            merge_count_in IN PLS_INTEGER
            ) RETURN PLS_INTEGER;

   FUNCTION get_merge_insert_count RETURN PLS_INTEGER;

   FUNCTION get_merge_insert_count (
            merge_count_in in PLS_INTEGER
            ) RETURN PLS_INTEGER;

   PROCEDURE reset_counters;

END etl;
/

CREATE OR REPLACE PACKAGE BODY CRISPBATCH.ETL AS

   g_update_counter PLS_INTEGER NOT NULL := 0;
   g_insert_counter PLS_INTEGER NOT NULL := 0;

/*-------------- START OF FUNCTION merge_counter --------------------*/
FUNCTION merge_counter (
         action_in IN PLS_INTEGER DEFAULT c_inserting
         ) RETURN PLS_INTEGER IS
BEGIN
   CASE action_in
      WHEN c_updating
         THEN g_update_counter := g_update_counter + 1;
      WHEN c_inserting
         THEN g_insert_counter := g_insert_counter + 1;
      ELSE
         RAISE PROGRAM_ERROR;
   END CASE;
   RETURN 0;
END merge_counter;

/*----------- START OF FUNCTION get_merge_update_count V1 ---------------*/
FUNCTION get_merge_update_count
   RETURN PLS_INTEGER is
BEGIN
   RETURN g_update_counter;
END get_merge_update_count;

/*----------- START OF FUNCTION get_merge_update_count V2 ---------------*/
FUNCTION get_merge_update_count (
         merge_count_in IN PLS_INTEGER
         ) RETURN PLS_INTEGER IS
BEGIN
   RETURN NVL( merge_count_in - g_insert_counter, 0 );
END get_merge_update_count;

/*----------- START OF FUNCTION get_merge_insert_count V1 ---------------*/
FUNCTION get_merge_insert_count
   RETURN PLS_INTEGER IS
BEGIN
   RETURN g_insert_counter;
END get_merge_insert_count;

/*----------- START OF FUNCTION get_merge_insert_count V2 ---------------*/
FUNCTION get_merge_insert_count (
         merge_count_in IN PLS_INTEGER
         ) RETURN PLS_INTEGER IS
BEGIN
   RETURN NVL( merge_count_in - g_update_counter, 0 );
END get_merge_insert_count;

/*-------------- START OF FUNCTION reset_counters --------------------*/
PROCEDURE reset_counters IS
BEGIN
   g_update_counter := 0;
   g_insert_counter := 0;
END reset_counters;

END etl;
/

I need to track the number of inserted/updated records for tracking purposes in case the figure is not tally with source or something like that.

Thank you.
Re: PL/SQL for Insert/Update [message #633549 is a reply to message #633548] Sat, 21 February 2015 01:55 Go to previous messageGo to next message
Michel Cadot
Messages: 68776
Registered: March 2007
Location: Saint-Maur, France, https...
Senior Member
Account Moderator

Because you know the number of rows which should be inserted and updated?
And then what would we do with that information? You will not know which rows do not match with what you expect.
How many times the discrepancy will happen?
Is this worth the treatment takes 10 times longer?

Re: PL/SQL for Insert/Update [message #633550 is a reply to message #633548] Sat, 21 February 2015 02:06 Go to previous messageGo to next message
aimy
Messages: 225
Registered: June 2006
Senior Member
But now I am getting a mysterious error of "ORA-12899: value too large for column "CRISPADM"."CAMS_JDIARY_JURISAGENCY"."D_TEXT" (actual: 4009, maximum: 4000)" whereas when I desc the table source SQL> desc JURISAGENCY.JDIARY@crisp_cams, it is clearly showing D_TEXT VARCHAR2(4000) even the current one.

Do you guys have any idea on this?

Thank you.
Re: PL/SQL for Insert/Update [message #633551 is a reply to message #633550] Sat, 21 February 2015 02:10 Go to previous messageGo to next message
Michel Cadot
Messages: 68776
Registered: March 2007
Location: Saint-Maur, France, https...
Senior Member
Account Moderator

You just try to store 4009 bytes into a 4000 bytes field.

Re: PL/SQL for Insert/Update [message #633552 is a reply to message #633551] Sat, 21 February 2015 02:12 Go to previous messageGo to next message
aimy
Messages: 225
Registered: June 2006
Senior Member
Michel Cadot wrote on Sat, 21 February 2015 16:10

You just try to store 4009 bytes into a 4000 bytes field.


But how could such 4009 bytes exist in the source table with D_text varchar2(4000) as well?
Re: PL/SQL for Insert/Update [message #633553 is a reply to message #633552] Sat, 21 February 2015 02:14 Go to previous messageGo to next message
Michel Cadot
Messages: 68776
Registered: March 2007
Location: Saint-Maur, France, https...
Senior Member
Account Moderator

Different character sets.

Re: PL/SQL for Insert/Update [message #633554 is a reply to message #633553] Sat, 21 February 2015 02:21 Go to previous messageGo to next message
aimy
Messages: 225
Registered: June 2006
Senior Member
I see.

Thanks.

So what is the best solution for this since 4000 is already the maximum length for varchar2?

Thank you.
Re: PL/SQL for Insert/Update [message #633555 is a reply to message #633554] Sat, 21 February 2015 02:29 Go to previous messageGo to next message
Michel Cadot
Messages: 68776
Registered: March 2007
Location: Saint-Maur, France, https...
Senior Member
Account Moderator

Best? What could be best when you cannot store your data?
Truncate?
Translate some characters into other ones which use less bytes?

[Updated on: Sat, 21 February 2015 02:30]

Report message to a moderator

Re: PL/SQL for Insert/Update [message #633560 is a reply to message #633555] Sat, 21 February 2015 08:50 Go to previous messageGo to next message
aimy
Messages: 225
Registered: June 2006
Senior Member
I mean some other data types that could be equivalent to that varchar2(4000).

Anyway your point of translating character might be useful. Can you show me some example whereby the translated length and the original length display the same data?

Thank you.
Re: PL/SQL for Insert/Update [message #633561 is a reply to message #633181] Sat, 21 February 2015 08:51 Go to previous messageGo to next message
EdStevens
Messages: 1377
Registered: September 2013
Senior Member
Just as an aside to all the other comments, I notice this bug in your code:

from CSM_DAILY_CKC where system_date='13/12/2014';


Here you are comparing a DATE (the column SYSTEM_DATE) to a string, forcing an implied type conversion that depends on an NLS_DATE_FORMAT setting over which you have no control. You should do an explicit comparison, like this:

from CSM_DAILY_CKC where system_date=to_date('13/12/2014');


And if SYSTEM_DATE is not a DATE, that's even worse.

It's also highly suspicious that you are comparing against a hard-coded date value at all. What's so magic about Dec 13, 2014, that it will never change?
Re: PL/SQL for Insert/Update [message #633563 is a reply to message #633561] Sat, 21 February 2015 09:01 Go to previous messageGo to next message
Littlefoot
Messages: 21826
Registered: June 2005
Location: Croatia, Europe
Senior Member
Account Moderator
What if that date string was 11/12/2014? Is TO_DATE enough, or would it be better to use appropriate format mask? Because, what is "11" and what is "12"? Which one is a day, and which one is a month?

P.S. Forgot about the DATE literal which uniquely describes date format, 'YYYY-MM-DD'. It is NOT convenient for me, we use 'DD.MM.YYYY', but I can live with it as I *know* that DATE literal is always the same.

[Updated on: Sat, 21 February 2015 09:58]

Report message to a moderator

Re: PL/SQL for Insert/Update [message #633566 is a reply to message #633560] Sat, 21 February 2015 13:28 Go to previous messageGo to next message
Michel Cadot
Messages: 68776
Registered: March 2007
Location: Saint-Maur, France, https...
Senior Member
Account Moderator

Quote:
Can you show me some example whereby the translated length and the original length display the same data?


If you translate then you have not the same data but you may be able to translate in a way which is acceptable.
For instance, in French "é" takes 1 byte in our usual character set but 3 bytes in AL32UTF8; however you can translate "é" to "e" which is acceptable for us even if it is not correct but with the profit that "e" is coded in 1 byte whatever is the character set.
However, this is not possible with all target character sets; for instance, if it is AL16UTF16 all characters are on 2 bytes and so you can't decrease the target size in any way.

Re: PL/SQL for Insert/Update [message #633649 is a reply to message #633561] Tue, 24 February 2015 00:11 Go to previous messageGo to next message
aimy
Messages: 225
Registered: June 2006
Senior Member
EdStevens wrote on Sat, 21 February 2015 22:51
Just as an aside to all the other comments, I notice this bug in your code:

from CSM_DAILY_CKC where system_date='13/12/2014';


Here you are comparing a DATE (the column SYSTEM_DATE) to a string, forcing an implied type conversion that depends on an NLS_DATE_FORMAT setting over which you have no control. You should do an explicit comparison, like this:

from CSM_DAILY_CKC where system_date=to_date('13/12/2014');


And if SYSTEM_DATE is not a DATE, that's even worse.

It's also highly suspicious that you are comparing against a hard-coded date value at all. What's so magic about Dec 13, 2014, that it will never change?

Haha thanks a lot for your concern.

Well, that 13 December 2014 is only for my testing purpose and yes the system_Date in this case is stored as string Wink

Thanks anyway. Really appreciate your concern.
Re: PL/SQL for Insert/Update [message #633650 is a reply to message #633563] Tue, 24 February 2015 00:12 Go to previous messageGo to next message
aimy
Messages: 225
Registered: June 2006
Senior Member
Thanks LittleFoot. Smile
Re: PL/SQL for Insert/Update [message #633652 is a reply to message #633566] Tue, 24 February 2015 00:39 Go to previous messageGo to next message
aimy
Messages: 225
Registered: June 2006
Senior Member
Thanks a lot for your precise example.

Based on your sample given, it is proven that these two queries yields 2 different results:
select LENGTHB('é') from dual@crisp_cams; - 1

select LENGTHB('é') from dual; - 3


So, I am planning to use the built-in Oracle function CONVERT to solve this issue.

By the way, where would I get the full list of characters that have this kind of issue?

And what would the character be if it is stored inside the target (AL32UTF8) from source (WE8MSWIN1252)? Would them be truncated to '?'

Thank you.

[Updated on: Tue, 24 February 2015 00:39]

Report message to a moderator

Re: PL/SQL for Insert/Update [message #633655 is a reply to message #633652] Tue, 24 February 2015 01:22 Go to previous messageGo to next message
Michel Cadot
Messages: 68776
Registered: March 2007
Location: Saint-Maur, France, https...
Senior Member
Account Moderator

Quote:
So, I am planning to use the built-in Oracle function CONVERT to solve this issue.


You can use Oracle TRANSLATE function. For instance for WE8MSWIN1252 and French it is:
TRANSLATE(col,
          'ƒŠŒŽšœžŸÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæçèéêëìíîïøðñòóôõöùúûüýÿÞþ',
          'fSEZsezYAAAAAAECEEEEIIIIDNOOOOOOUUUUYBaaaaaaeceeeeiiiioonooooouuuuyy')


Quote:
By the way, where would I get the full list of characters that have this kind of issue?


These are all the characters with code point >= 128, you can use the ASCII function to get the code point:
SQL> select ascii('é') from dual;
ASCII('é')
----------
       130

1 row selected.

And CHR function to get the character from its code point. So you can use a loop like:
SQL> begin
  2    for i in 128..255 loop
  3      dbms_output.put(chr(i));
  4    end loop;
  5    dbms_output.new_line;
  6  end;
  7  /
ÇüéâäàåçêëèïîìÄÅÉæÆôöòûùÿÖÜø£Ø×ƒáíóúñѪº¿®... (output truncated)

PL/SQL procedure successfully completed.


[Updated on: Tue, 24 February 2015 01:24]

Report message to a moderator

Re: PL/SQL for Insert/Update [message #633658 is a reply to message #633655] Tue, 24 February 2015 01:36 Go to previous messageGo to next message
aimy
Messages: 225
Registered: June 2006
Senior Member
Thanks a lot Michael.

But I'm just curious, is it not OK to do this instead...

SELECT '@#$*%ÄÊÍÓØABCDE' original_text, CONVERT('@#$*%ÄÊÍÓØABCDE', 'WE8MSWIN1252', 'AL32UTF8') modified_text,
lengthb('@#$*%ÄÊÍÓØABCDE') original_length,
lengthb(CONVERT('@#$*%ÄÊÍÓØABCDE', 'WE8MSWIN1252', 'AL32UTF8')) modified_length
from dual;

Thank you.
Re: PL/SQL for Insert/Update [message #633662 is a reply to message #633658] Tue, 24 February 2015 02:27 Go to previous messageGo to next message
Michel Cadot
Messages: 68776
Registered: March 2007
Location: Saint-Maur, France, https...
Senior Member
Account Moderator

Compare (from a couple of French words):
SQL> with
  2    data as (
  3      select 'ambiguïté séquoia après déjà français' original_text
  4      from dual
  5    )
  6  SELECT original_text,
  7         CONVERT(original_text, 'WE8MSWIN1252', 'AL32UTF8') modified_text
  8  from data
  9  /
ORIGINAL_TEXT                            MODIFIED_TEXT
---------------------------------------- ----------------------------------------
ambiguïté séquoia après déjà français    ambigu¿t¿ s¿quoia apr¿s d¿j¿ fran¿ais

With:
SQL> with
  2    data as (
  3      select 'ambiguïté séquoia après déjà français' original_text
  4      from dual
  5    )
  6  SELECT original_text,
  7         translate(original_text,
  8                   'ƒŠŒŽšœžŸÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæçèéêëìíîïøðñòóôõöùúûüýÿÞþ',
  9                   'fSEZsezYAAAAAAECEEEEIIIIDNOOOOOOUUUUYBaaaaaaeceeeeiiiioonooooouuuuyy')
 10           modified_text
 11  from data
 12  /
ORIGINAL_TEXT                            MODIFIED_TEXT
---------------------------------------- ----------------------------------------
ambiguïté séquoia après déjà français    ambiguite sequoia apres deja francais

The former turns to be unreadable when the later is understandable by any French speaking person.

[Updated on: Tue, 24 February 2015 02:35]

Report message to a moderator

Re: PL/SQL for Insert/Update [message #633663 is a reply to message #633662] Tue, 24 February 2015 02:33 Go to previous messageGo to next message
aimy
Messages: 225
Registered: June 2006
Senior Member
Thanks a lot Michael. Precise explanation with examples.

As you might already seen, the reason I asked is because if I have to use translate, I need to convert every single character to the equivalent right? That is what I'm trying to skip.
Re: PL/SQL for Insert/Update [message #633664 is a reply to message #633663] Tue, 24 February 2015 02:40 Go to previous messageGo to next message
Michel Cadot
Messages: 68776
Registered: March 2007
Location: Saint-Maur, France, https...
Senior Member
Account Moderator

Only the characters that are not in 0-127 code points that is at most 128 characters (70 for WE8MSWIN1252) I showed you how to get them and then you have to associate them with a new character, you do this once, it will take less than 10 minutes and the result is a good conversion.
I think it is worth to do it, don't you think?

[Updated on: Tue, 24 February 2015 02:41]

Report message to a moderator

Re: PL/SQL for Insert/Update [message #633668 is a reply to message #633181] Tue, 24 February 2015 02:55 Go to previous messageGo to previous message
aimy
Messages: 225
Registered: June 2006
Senior Member
Definitely it's worth it coz you have already helped me out those list of 70 characters. Haha...

Anyway, why I am getting only the 'f' replacement with my SqlTools (http://www.sqltools.net/) output?

/forum/fa/12501/0/

Thank you.
Previous Topic: Table Updates
Next Topic: using function in select statement giving same value
Goto Forum:
  


Current Time: Thu Aug 27 00:39:17 CDT 2026