The below procedure is taking almost5 hours to complete 130000 records. Have tried optimizing it. Is there way to optimize it more?
If i commit after each "for all" , does that increase the performance?
Also i have given a limit of 1000 records, so here it is committing for 1000 record processing or when the count becomes 0?
database version : oracle 10g
I am running the below procedure :
create or replace PROCEDURE WeeklyClearQwAcctActivityLoad
AS
v_no_of_Days_retention number;
jobname VARCHAR2 (30);
loadstartdt DATE;
rcdcnt number;
errorcode NUMBER;
errormsg VARCHAR2 (100);
/* Identify those records for which all CUST_ACCT_ID under the same Parent_id are closed (before retention days ) */
CURSOR getdataforhist
IS
select /*+ parallel(qa2,128) */ CUST_ACCT_ID from qw_account qa2
where exists
( select /*+ parallel(qaa2,128) */ 1 from QW_ACCT_ACTIVITY qaa2
where qaa2.cust_acct_id = qa2.cust_acct_id
and qaa2.actvy_end_date < sysdate - v_no_of_Days_retention
)
and not exists
(
select 1 from (
select /*+ parallel(qa,128) */ qa.PARENT_ID pidd from qw_account qa
where exists
( select /*+ parallel(qaa,128) */ 1 from QW_ACCT_ACTIVITY qaa
where qaa.cust_acct_id = qa.cust_acct_id
and qaa.actvy_end_date > sysdate - v_no_of_Days_retention
)
) pp where pp.pidd = qa2.PARENT_ID
);
TYPE t_getDataForHist IS TABLE OF qw_acct_activity.cust_acct_id%TYPE;
l_getDataForHist t_getDataForHist;
CURSOR orph_product
IS
SELECT /*+ parallel( ram , 128) */ ram.rcr_prod_acct_id
FROM rcr_acct_mapping ram
WHERE 1=1
AND ram.cust_acct_id IS NOT NULL
AND EXISTS
( SELECT /*+ parallel( rap , 128) */ 1
FROM rcr_acct_profile rap
WHERE rap.rcr_prod_acct_id = ram.rcr_prod_acct_id
AND rap.cust_acct_id = ram.cust_acct_id
AND rap.prod_acct_status in ('ACTIVE','INACTIVE','SUSPENDED')
)
AND NOT EXISTS
( SELECT /*+ parallel( qaa , 128 */ 1
FROM qw_acct_activity qaa
WHERE qaa.cust_acct_id = ram.cust_acct_id
);
TYPE t_orph_product is table of rcr_acct_mapping.rcr_prod_acct_id%TYPE;
l_orph_product t_orph_product;
cnt number default 0;
BEGIN
jobname := 'WEEKLY_CLEAN_QW_ACCT_ACTIVITY';
loadstartdt := SYSDATE;
rcdcnt := 0;
INSERT INTO rcr_stage_audit (job_name,load_start_date,load_end_date,record_cnt,processed,process_date,process_cnt,ignore_cnt)
VALUES (jobname,loadstartdt,NULL,NULL,'N',loadstartdt,NULL,NULL );
COMMIT;
BEGIN
SELECT VALUE into v_no_of_Days_retention
FROM rcr_online_svc_app_config
WHERE NAME = 'noofdaystoenddateqwacctactivity';
EXCEPTION
WHEN NO_DATA_FOUND
THEN
errorcode := SQLCODE;
errormsg := 'no of days to end date qw_accta_ctivity is not defined in rcr_code_translation table';
raise_application_error (-20101, errorcode || ' - ' || errormsg, TRUE);
END;
OPEN getDataForHist;
LOOP
FETCH getdataforhist BULK COLLECT INTO l_getdataforhist LIMIT 1000;
--EXIT WHEN getdataforhist%NOTFOUND ;
EXIT WHEN l_getdataforhist.COUNT = 0;
-- FORALL indx IN 1 .. l_getdataforhist.count
-- insert into TEMPSLOT (CUST_ACCT_ID) values ( l_getdataforhist(indx) ) ;
FORALL indx1 IN 1 .. l_getdataforhist.count
INSERT INTO qw_acct_activity_hist
SELECT qaa.*, SYSDATE
FROM qw_acct_activity qaa
WHERE CUST_ACCT_ID = ( l_getdataforhist(indx1) );
FORALL indx2 IN 1 .. l_getdataforhist.count
DELETE FROM qw_acct_activity
WHERE CUST_ACCT_ID = ( l_getdataforhist(indx2) );
rcdcnt := rcdcnt + sql%rowcount;
COMMIT;
END LOOP;
CLOSE getDataForHist;
--- Clean porduct tables for orphan CUST_ACCT_ID
OPEN orph_product;
LOOP
FETCH orph_product BULK COLLECT INTO l_orph_product LIMIT 1000;
EXIT WHEN l_orph_product.COUNT = 0;
FORALL indx10 IN 1 .. l_orph_product.COUNT
INSERT INTO rcr_acct_mapping_hist
SELECT a.*
FROM rcr_acct_mapping a
WHERE rcr_prod_acct_id = ( l_orph_product(indx10) );
FORALL indx11 IN 1 .. l_orph_product.COUNT
DELETE FROM rcr_acct_mapping WHERE rcr_prod_acct_id = ( l_orph_product(indx11) );
FORALL indx12 IN 1 .. l_orph_product.COUNT
DELETE FROM rcr_addt_acct_prof_detail WHERE rcr_prod_acct_id = ( l_orph_product(indx12) );
FORALL indx13 IN 1 .. l_orph_product.COUNT
DELETE FROM rcr_acct_profile_detail WHERE rcr_prod_acct_id = ( l_orph_product(indx13) );
FORALL indx14 IN 1 .. l_orph_product.COUNT
DELETE FROM rcr_acct_profile WHERE rcr_prod_acct_id = ( l_orph_product(indx14) );
COMMIT;
END LOOP;
close orph_product;
UPDATE rcr_stage_audit
SET load_end_date = SYSDATE,
record_cnt = rcdcnt,
processed = 'Y'
WHERE job_name = jobname
AND process_date = loadstartdt
AND load_start_date = loadstartdt;
COMMIT;
EXCEPTION
WHEN OTHERS
THEN
errorcode := SQLCODE;
errormsg := substr(sqlerrm,1,255);
raise_application_error (-20102, errorcode || ' - ' || errormsg, TRUE);
END WeeklyClearQwAcctActivityLoad;
One suggestion that I would recommend to you is to avoid explicit looping and cursors. It leads to poor performance, especially when you can directly use
insert into <table_name>(columns) select <your_query_goes_here>
This is most certainly going to perform faster than your looping constructs. In fact you can reproduce this behavior with simple benchmarks in a table generated with a million records.
So in short try to avoid looping and your code would also look more readable and less prone to errors.
I did a benchmark once where out of 10 million records only about 12,000 had to be updated and timing of explicit looping vs implicit looping lead was 1 minute 10 seconds v/s 1 second.
trace it using oracle tools like as oradebug or dbms_monitor and using tkprof or other analysis tools check the trace file.do the below:
1.extract the sid
2.in sqlplus run: oradebug setorapid xxxx
3.in sqlplus run: oradebug tracefile_name
4.after the process complete in os run tkprof on trace file.(other tools is available also).
5.in trace file check long sections and work on them
Related
I have below procedure giving me error as
Error(17,3): PLS-00103: Encountered the symbol "FOR" when expecting one of the following: ( - + case mod new not null select with continue avg count current exists max min prior sql stddev sum variance execute fora
Here is the procedure.
PROCEDURE DeactiveUsers (
P_DeactiveUsers_OUT OUT SYS_REFCURSOR ) AS
BEGIN
OPEN P_DeactiveUsers_OUT FOR
for P_DeactiveUsers_Lst in(
select * from (
select username, MAX(TRANSACTION_DATE) As last_login_date
from r4g_application_activity_log
Group By username
) where last_login_date <= sysdate-90
order by 2 desc
)
update r4g_application_activity_log
set ISACTIVE = 1
where USERNAME = P_DeactiveUsers_OUT.username;
EXCEPTION
WHEN no_data_found THEN
INS_UMS_ERRORLOG(SQLCODE||' : '||SUBSTR(SQLERRM, 1, 200),null,'DeactiveUsers',null,null,null,'DB : DeactiveUsers','Scheduler - UMS_DeactiveUser');
WHEN others THEN
INS_UMS_ERRORLOG(SQLCODE||' : '||SUBSTR(SQLERRM, 1, 200),null,'DeactiveUsers',null,null,null,'DB : DeactiveUsers','Scheduler - UMS_DeactiveUser');
END DeactiveUsers;
Hm, yes - procedure is here, but - what is its purpose? The way you put it, it seems that it shouldn't return anything so you don't really want to open a ref cursor, but use a cursor FOR loop which then updates the log table.
If that's so,
remove OUT parameter
remove OPEN clause (btw. you've got two FORs)
use FOR loop
remove WHEN NO_DATA_FOUND as there's nothing that could raise it
PROCEDURE DeactiveUsers -- (P_DeactiveUsers_OUT OUT SYS_REFCURSOR)
AS
BEGIN
--OPEN P_DeactiveUsers_OUT FOR
FOR P_DeactiveUsers_Lst
IN ( SELECT *
FROM ( SELECT username, MAX (TRANSACTION_DATE) AS last_login_date
FROM r4g_application_activity_log
GROUP BY username)
WHERE last_login_date <= SYSDATE - 90
ORDER BY 2 DESC)
LOOP
UPDATE r4g_application_activity_log
SET ISACTIVE = 1
WHERE USERNAME = P_DeactiveUsers_OUT.username;
END LOOP;
EXCEPTION
-- WHEN no_data_found THEN
-- INS_UMS_ERRORLOG(SQLCODE||' : '||SUBSTR(SQLERRM, 1, 200),null,'DeactiveUsers',null,null,null,'DB : DeactiveUsers','Scheduler - UMS_DeactiveUser');
WHEN OTHERS
THEN
INS_UMS_ERRORLOG (SQLCODE || ' : ' || SUBSTR (SQLERRM, 1, 200),
NULL,
'DeactiveUsers',
NULL,
NULL,
NULL,
'DB : DeactiveUsers',
'Scheduler - UMS_DeactiveUser');
END DeactiveUsers;
[EDIT]
As you want to return list of deactivated users, then you could do it as follows: instead of a ref cursor, just loop through result set, do the update and return a collection of deactivated users:
PROCEDURE deactiveusers (p_deactivataed OUT SYS.odcivarchar2list)
IS
l_deactivated SYS.odcivarchar2list := sys.odcivarchar2list ();
BEGIN
FOR cur_r
IN ( SELECT *
FROM ( SELECT username, MAX (TRANSACTION_DATE) AS last_login_date
FROM r4g_application_activity_log
GROUP BY username)
WHERE last_login_date <= SYSDATE - 90
ORDER BY 2 DESC)
LOOP
UPDATE r4g_application_activity_log
SET isactive = 1
WHERE username = cur_r.username;
l_deactivated.EXTEND;
l_deactivated (l_deactivated.LAST) := cur_r.username;
END LOOP;
p_deactivated := l_deactivated;
END;
You'd call it as e.g.
DECLARE
l_deactivated SYS.odcivarchar2list;
BEGIN
deactiveusers (l_deactivated);
FOR i IN l_deactivated.FIRST .. l_deactivated.LAST
LOOP
DBMS_OUTPUT.put_line (l_deactivated (i));
END LOOP;
END;
/
The below block tries to update a large table t1 with data found in table t2.It seems fine when i update for comment code that has 500 records but takes 30 minutes to update more than 1000 records. I tried the bulk collect update and index on comment code there is not much of time difference.
DECLARE
lv_row_count NUMBER(9) := 0;
lv_total_count NUMBER(9) := 0;
lv_commit_cnt SIMPLE_INTEGER:=0;
BEGIN
FOR rec in
(SELECT
a.t1_id,
a.t1_orig_code,
t2_orig_code,
a.t1_comment_code,
t2_code,
a.t1_restrict_update_ind,
t2_restrict_update_ind,
a.t1_data_origin,
t2_data_origin,
a.t1_purge_ind,
t2_purge_ind,
a.t1_created_date,
a.rowid
FROM t1 a
JOIN t2 ON t2_code = a.t1_comment_code
WHERE a.t1_comment_code in ('A','B','C','C1','D3')
AND ( a.t1_orig_code != t2_orig_code OR a.t1_restrict_update_ind !=t2_restrict_update_ind
OR a.t1_data_origin != t2_data_origin OR a.t1_purge_ind != t2_purge_ind)
)
LOOP
lv_total_count := lv_total_count + 1;
UPDATE t1
SET t1_ORIG_CODE= rec.t2_orig_code
t1_RESTRICT_UPDATE_IND = 'Y',,
t1_DATA_ORIGIN = rec.t2_data_origin,
t1_PURGE_IND =rec.t2_purge_ind
WHERE t1.rowid =rec.rowid ;
lv_commit_cnt:=lv_commit_cnt+1;
IF MOD(lv_commit_cnt,lv_limit)=0 THEN
lv_commit_cnt:=0;
COMMIT;
END IF;
dbms_output.put_line('a.t1_pidm -'||rec.t1_pidm ||
'a.t1_orig_code -'||rec.t1_orig_code ||'Updated');
END LOOP;
COMMIT;
dbms_output.put_line('Total_count- '||lv_total_count);
-- dbms_output.put_line('No record');
END;
Appreciate inputs on this .
No surprise it takes ages; row-by-row processing is slow-by-slow.
How about merge, instead?
merge into t1
using t2
on (t1.t1_comment_code = t2.t2_code)
when matched then update set
t1.t1_orig_code = t2.t2_orig_code,
t1.t1_restrict_update_ind = 'Y',
t1.t1_data_origin = t2.t2_data_origin,
t1.t1_purge_ind = t2.t2_purge_ind
where t1.t1_comment_code in ('A', 'B', 'C', 'C1', 'D3');
Just like that; no PL/SQL, no loop, no commit in the middle of the loop ... nothing. Just merge.
I have a procedure that runs a select ( I tested that is good returns 56 records )
then when I run a cursor I want to pass 3 fields to a function ( see above ) that will
lookup/select a record from a table that contains 15 million records ( 10 years worth ).
It returns a rowtype that I want to then extract the fields from this rowtype record to
run an insert with both the records from the 1st select and the additional fields acquired
from the lookup function.
If I run the procedure the console prints out my test msgs but when I try to run
select * bulk collect into v_tab_proc_claim_recs from v_processed_claim;
it doesn't compile due to Error(97,65): PL/SQL: ORA-00942: table or view does not exist
as if either of these are not Tables.
Am I doing this right... how can I do it, why can't it see the table I'm trying to extract to ?
Should I do this some other way..
Thanks for any help/suggestions :)
The function is below....
create or replace function get_processed_claim_rec(
p_provider VARCHAR2,
p_rx VARCHAR2,
p_record_no NUMBER
)
return i_idb.processed_claim%rowtype
as
l_claim_record i_idb.processed_claim%rowtype;
begin
select * into l_claim_record from i_idb.processed_claim
where source_date = p_provider
AND rx = p_rx
AND rec_no = p_record_no;
return(l_claim_record);
end;
And the procedure is....
create or replace PROCEDURE import_mailer_data
AS
-------------------------------
/**
for the lookup table
**/
v_processed_claim i_idb.processed_claim%rowtype;
TYPE proc_claim_recs IS TABLE OF v_processed_claim%ROWTYPE INDEX BY PLS_INTEGER;
v_tab_proc_claim_recs proc_claim_recs;
--------------------------------
CURSOR myCursor
IS
SELECT *
from
(
SELECT
j.create_date as open_date,
case when (j.create_date < (sysdate - 20) )
then 'POD'
else 'REG'
end as priority,
c.division,
c.unit,
--p.refill as days_supply,
--p.din_name,
'CM_JOHN' as log_code,
c.first_name,
c.last_name,
--p.UNLISTED_compound,
--p.intervention_code,
--p.substitution,
--p.confirm,
c.PROVIDER,
c.rx,
c.DISPENSE_DATE,
c.DIN,
c.QTY,
c.DIN_COST_PAID,
c.DISP_FEE_PAID,
c.PAID_AMOUNT,
c.SOURCE_DATE,
c.RECORD_NO,
c.RELATIONSHIP,
c.INSURER_NO,
c.GROUP_NO,
c.CERTIFICATE,
c.BIRTH_DATE,
c.USER_ID,
--p.rej_code --v_seq_no
rank() over
(
partition by c.provider, c.rx, c.record_no Order by c.provider desc, c.rx desc
) as RNK
FROM AUDITCOLLECTIONS.MAILER_CLAIMS c,
AUDITCOLLECTIONS.MAILER_JOBS j
WHERE MAILER_JOB_DETAIL_ID IN
(SELECT MAILER_JOB_DETAIL_ID
FROM AUDITCOLLECTIONS.MAILER_JOB_DETAILS
WHERE MAILER_JOB_ID IN
( SELECT MAILER_JOB_ID FROM AUDITCOLLECTIONS.MAILER_JOBS
)
)
AND ( c.PROVIDER, c.rx, c.record_no ) NOT IN
( SELECT provider, rx, rec_no FROM AUDITCOLLECTIONS.COLLECTION_AUDIT_STAGING
)
AND j.create_date > (sysdate - 30)
AND c.provider = '2010500042'
) A_Latest
where A_Latest.RNK = 1;
BEGIN
v_report_id := audit_load.create_loaded_report(v_report_type_id);
FOR curRec IN myCursor
LOOP
BEGIN
dbms_output.put_line ('===>>>> PRINTING TEST1 = ');
v_processed_claim := get_processed_claim_rec(curRec.PROVIDER, curRec.RX, curRec.RECORD_NO);
select * bulk collect into v_tab_proc_claim_recs from v_processed_claim;
END LOOP;
audit_load.update_status_to_loaded(v_report_id);
END import_mailer_data;
You can do this:
FOR curRec IN myCursor
LOOP
v_processed_claim :=
get_processed_claim_rec(curRec.PROVIDER, curRec.RX, curRec.RECORD_NO);
v_tab_proc_claim_recs (v_tab_proc_claim_recs.COUNT+1) := v_processed_claim;
END LOOP;
Or simplify to:
FOR curRec IN myCursor
LOOP
v_tab_proc_claim_recs (v_tab_proc_claim_recs.COUNT+1) :=
get_processed_claim_rec(curRec.PROVIDER, curRec.RX, curRec.RECORD_NO);
END LOOP;
I am trying to return the data through refcursor that is used in Select for update but I am not able to find any way. Can any one please guide me.
CREATE OR REPLACE PROCEDURE SELECT_SCHEDULED_REPORTS
(o_scheduledreports_cursor OUT SYS_REFCURSOR)
IS
CURSOR report_ids
IS
SELECT *
FROM dwp_rep_scheduler_t
WHERE SCHEDULE_ID IN
(SELECT SCHEDULE_ID
FROM
(SELECT *
FROM dwp_rep_scheduler_t a
WHERE status = 1
AND schedule_type = 1
ORDER BY a.start_date
)
WHERE ROWNUM <= 5
) FOR UPDATE OF status;
BEGIN
FOR report_id IN report_ids
LOOP
UPDATE dwp_rep_scheduler_t SET status = 2 WHERE CURRENT OF report_ids;
END LOOP;
COMMIT;
-- can I do something like open o_scheduledreports_cursor for report_ids
END;
/
As suggested by #Lalit Kumar B, i tried following but now it compiles with error as "PLS-00221: 'O_SCHEDULEDREPORTS_CURSOR' is not a procedure or is undefined"
CREATE OR REPLACE PROCEDURE SELECT_SCHEDULED_REPORTS (
o_scheduledreports_cursor OUT SYS_REFCURSOR)
IS
begin
open o_scheduledreports_cursor for
SELECT *
FROM dwp_rep_scheduler_t
WHERE SCHEDULE_ID IN (SELECT SCHEDULE_ID
FROM ( SELECT *
FROM dwp_rep_scheduler_t a
WHERE status = 1 AND schedule_type = 1
ORDER BY a.start_date)
WHERE ROWNUM <= 5)
FOR UPDATE OF status;
BEGIN
FOR report_id IN o_scheduledreports_cursor
LOOP
UPDATE dwp_rep_scheduler_t
SET status = 2
WHERE CURRENT OF report_ids;
END LOOP;
COMMIT;
END;
END SELECT_SCHEDULED_REPORTS;
/
Select for update is a programming tool which you would use to ensure that no one else updates your data. Inside you PL/SQL, you would use the rows locked for update, and then do the required transaction. Commit your changes, Oracle would release the lock mode 3.
You could simply do,
Open cur for
select column_list from table where ....
Not that specific, but, this asktom link about select for ...update is a good read https://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:927629362932
You might also be interested to know few interesting things about the clause, here is my take on a myth about select..for update http://lalitkumarb.wordpress.com/2014/09/04/a-myth-about-row-exclusive-table-lock-on-select-for-update-with-no-rows/
Hope it helps!
create or replace procedure Proc_1(P_IN_TABLE_NAME VARCHAR2)
AS
CURSOR T_FACT
IS
SELECT T_ID,T_VER,D_T_ID
from O_T_FACT
where T_ID is not null
and T_VER is not null;
TYPE call_tab IS TABLE OF O_T_FACT%rowtype;
BEGIN
IF P_IN_TABLE_NAME ='G_FACT' THEN
OPEN T_FACT;
LOOP
EXIT WHEN T_FACT%NOTFOUND ;
FETCH T_FACT BULK COLLECT INTO call_data_rec LIMIT no_of_rec;
EXIT WHEN call_data_rec.count = 0;
FOR j IN 1..call_data_rec.COUNT
loop
UPDATE G_FACT GL set
GL.T_ID = call_data_rec(j).T_ID,
GL.T_VER =call_data_rec(j).T_VER,
GL.TRANS_FLAG='Y'
WHERE GL.G_T_ID = call_data_rec(j).D_T_ID
AND GL.T_ID IS NULL
AND GL.T_VER IS NULL;
rec_count := rec_count + 1;
if mod(rec_count,10000) = 0 then
commit;
end if;
end loop;
end loop;
CLOSE T_FACT;
END IF;
End;
This particular procedure is taking long time, is there any other way to write this? Can this be done in a single update statement?
As suggested below I have tired for all but its giving error as
PLS-00436: implementation restriction: cannot reference fields of BULK In-BIND table of records
New Code with For all
create or replace procedure Proc_update_T_ID(P_IN_TABLE_NAME VARCHAR2)
AS
no_of_rec number := 1000;
CURSOR T_and_V_FACT
IS
SELECT O_T_FACT.T_ID, O_T_FACT.T_VER, O_T_FACT.Downstream_T_ID, G_FACT.rowid row_id,
From O_T_FACT, G_FACT
WHERE O_T_FACT.T_ID IS NOT NULL AND G_FACT.G_T_ID = O_T_FACT.Downstream_T_ID
AND T_VER is not null
AND G_FACT.T_VER IS NULL;
TYPE call_tab IS TABLE OF T_and_V_FACT%rowtype index by binary_integer;
call_data_rec call_tab;
BEGIN
IF P_IN_TABLE_NAME ='G_FACT' THEN
IF T_and_V_FACT%ISOPEN THEN
CLOSE T_and_V_FACT;
END IF;
open T_and_V_FACT;
LOOP
FETCH T_and_V_FACT BULK COLLECT
INTO call_data_rec LIMIT no_of_rec;
FORALL j IN call_data_rec.FIRST .. call_data_rec.LAST
UPDATE G_FACT GL set
GL.T_ID = call_data_rec(j).T_ID,
GL.T_VER =call_data_rec(j).T_VER,
GL.TRANS_FLAG='Y'
WHERE GL.rowid = call_data_rec(j).row_id;
COMMIT;
call_data_rec.DELETE;
EXIT WHEN T_and_V_FACT%NOTFOUND;
END LOOP;
CLOSE T_and_V_FACT;
End if;
END Proc_1;
It looks to me like you could rewrite this into a single statement like so:
update G_FACT GL
set (GL.T_ID, GL.T_VER, GL.TRANS_FLAG) =
(select T_ID,T_VER, 'Y'
from O_T_FACT F
where F.T_ID is not null
and F.T_VER is not null
and GL.G_T_ID = F.D_T_ID)
where exists (select null
from O_T_FACT F
where F.T_ID is not null
and F.T_VER is not null
and GL.G_T_ID = F.D_T_ID)
and GL.T_ID is null
and GL.T_VER is null;
If this doesn't work, then you should be able to get significant gains by converting your for loop into a forall statement:
FORALL j IN 1..call_data_rec.COUNT
UPDATE G_FACT GL set
GL.T_ID = call_data_rec(j).T_ID,
GL.T_VER =call_data_rec(j).T_VER,
GL.TRANS_FLAG='Y'
WHERE GL.G_T_ID = call_data_rec(j).D_T_ID
AND GL.T_ID IS NULL
AND GL.T_VER IS NULL;
Also, rethink whether you need that commit in the loop. Including this will:
Slow your processing down
Increase the chance of hitting ORA-1555s
Possibly leave your data in an inconsistent state
I think you should change your CURSOR and hit UPDATE not in a loop statement
Also I prefer using RowID for update statements and LIMIT for fetch size of cursors and FOR ALL for maximum performance and memory management.
We need to define a new type:
CREATE TYPE my_rec AS OBJECT
( T_ID NUMBER
, T_VER number
, row_id UROWID)
);
Then using Proc_1 may serve:
create or replace procedure Proc_1(P_IN_TABLE_NAME VARCHAR2)
AS
no_of_rec number := 1000;
CURSOR T_and_V_FACT
IS
SELECT T_FACT.T_ID, T_FACT.T_VER, T_FACT.D_T_ID, G_FACT.rowid row_id,
From O_T_FACT, G_FACT
WHERE T_FACT.T_ID IS NOT NULL AND G_FACT.G_T_ID = O_T_FACT.D_T_ID
AND T_VER is not null
-- AND G_FACT.T_ID IS NULL -- not requierd
AND G_FACT.T_VER IS NULL;
TYPE call_tab IS TABLE OF T_and_V_FACT%rowtype index by binary_integer;
call_data_rec call_tab;
BEGIN
IF P_IN_TABLE_NAME ='G_FACT' THEN
IF T_and_V_FACT%ISOPEN THEN
CLOSE T_and_V_FACT;
END IF;
open T_and_V_FACT;
LOOP
FETCH T_and_V_FACT BULK COLLECT
INTO call_data_rec LIMIT no_of_rec;
FORALL j IN call_data_rec.FIRST .. call_data_rec.LAST
UPDATE G_FACT GL set
GL.T_ID = TREAT(call_data_rec(j) AS my_rec).T_ID,
GL.T_VER =TREAT(call_data_rec(j) AS my_rec).T_VER,
GL.TRANS_FLAG='Y'
WHERE GL.rowid = TREAT(call_data_rec(j) AS my_rec).row_id;
COMMIT;
call_data_rec.DELETE;
EXIT WHEN T_and_V_FACT%NOTFOUND;
END LOOP;
CLOSE T_and_V_FACT;
End if;
END Proc_1;
I have edited some parts based on #Ben's comment.
Also i did some changes based on versions 9i and 10g
Restriction of using TREAT has been removed in 11g