I want to insert from one table to other with some edits in values. source table has 20,000,000 records and insert and commit them impossible . so i write a procedure to commit each 1000 insert in a loop. but it does not work. what is the problem?
CREATE OR REPLACE PROCEDURE CONVERT_INTO_K2 IS
batch_size number;
row_num number;
CURSOR trans IS
select rownum,KWP_Trns_Utils.Get_Acnt_From_TRACNTID(t.TRPAN),
t.TRPRRN, t.TRSIDE,t.switchcode,t.kcb_switchcode,
t.TERMID, t.TRTERMID,t.TRPOSCCOD,t.TRCAID,
t.TRMTI,t.TRPRCOD,t.TR87RSPCOD,t.TRMSGRSN,
t.TRREVFLG,t.TRSETTMD,t.TRCURCOD,t.TRAMNT,
t.TRORGAMNT,t.TRCHBLCUR,t.TRFEEAMNT,t.TRCHBLAMNT,
t.TRFRWIIC,t.TRACQIIC,t.TRRECIIC,t.PTRTRCNO,
t.BTRRN, t.TRTRACEDT, t.TRRSPDT, t.TRLDATE,
t.TRLTIME, t.BAID, t.BADATE,t.TRACNTID1,
KWP_Trns_Utils.Get_Acnt_From_TRACNTID(t.TRACNTID2),
t.ATJHNO,t.ATJHDAT, t.TRORGDTELM,t.TRADDATA,
KWP_Trns_Utils.Get_Acnt_From_TRACNTID(t.TRACNTID2),
Trn_ID.Nextval, Trn_diag.Nextval,
1, 1, 1, 1, 1, 1, 1, 1, 1
from P912.KCTRNS t
where t.trprcod != 47
and rownum < 200;
BEGIN
batch_size := 0;
row_num:=0;
FOR rec IN trans LOOP
batch_size := batch_size + 1;
row_num := row_num + 1;
if MOD( row_num, 1000 ) != 0 then
insert into P912.KCTRNS2
(srcpan,rfrnnum, trnsid,swchcod, prswchcod,intrtrmid,
xtrntrmid,trmcod, aptrid,msgtypidnt,trntyp,
rspcod, msqrsn,rvrsflg,sttlsts,currcod,
amt,origamt,crdhldrcurrcod,feeamt,
crdhldrdiscamt, isurcrdinstid,acqrcrdinstid,
rcvrcrdinstid,trcnum,intrrfrnnum,
rcvdt,rspdt, prrcvdtsctn,prrcvtmsctn,
btchid, btchiopendt,firsacctnum,
scndacctnum,docnum,docdt, origdtelmt,
dditdat,dstpan, id, diag, mngcod,
funccod, sttlcod,trnres, custno,
crdlesstrcno,accttyp1,accttyp2,chnltyp)
Values
(KWP_Trns_Utils.Get_Acnt_From_TRACNTID(t.TRPAN),
t.TRPRRN, t.TRSIDE,t.switchcode,t.kcb_switchcode,
t.TERMID, t.TRTERMID,t.TRPOSCCOD,t.TRCAID,
t.TRMTI,t.TRPRCOD,t.TR87RSPCOD,t.TRMSGRSN,
t.TRREVFLG,t.TRSETTMD,t.TRCURCOD,t.TRAMNT,
t.TRORGAMNT,t.TRCHBLCUR,t.TRFEEAMNT,t.TRCHBLAMNT,
t.TRFRWIIC,t.TRACQIIC,t.TRRECIIC,t.PTRTRCNO,
t.BTRRN, t.TRTRACEDT, t.TRRSPDT, t.TRLDATE,
t.TRLTIME, t.BAID, t.BADATE,t.TRACNTID1,
KWP_Trns_Utils.Get_Acnt_From_TRACNTID(t.TRACNTID2),
t.ATJHNO,t.ATJHDAT, t.TRORGDTELM,t.TRADDATA,
KWP_Trns_Utils.Get_Acnt_From_TRACNTID(t.TRACNTID2),
Trn_ID.Nextval, Trn_diag.Nextval,
1, 1, 0, 1, 1, 1, 1, 1, 1);
else
insert into P912.KCTRNS2
(srcpan,rfrnnum, trnsid,swchcod, prswchcod,intrtrmid,
xtrntrmid,trmcod, aptrid,msgtypidnt,trntyp,
rspcod, msqrsn,rvrsflg,sttlsts,currcod,
amt,origamt,crdhldrcurrcod,feeamt,
crdhldrdiscamt, isurcrdinstid,acqrcrdinstid,
rcvrcrdinstid,trcnum,intrrfrnnum,
rcvdt,rspdt, prrcvdtsctn,prrcvtmsctn,
btchid, btchiopendt,firsacctnum,
scndacctnum,docnum,docdt, origdtelmt,
dditdat,dstpan, id, diag, mngcod,
funccod, sttlcod,trnres, custno,
crdlesstrcno,accttyp1,accttyp2,chnltyp)
Values
(KWP_Trns_Utils.Get_Acnt_From_TRACNTID(t.TRPAN),
t.TRPRRN, t.TRSIDE,t.switchcode,t.kcb_switchcode,
t.TERMID, t.TRTERMID,t.TRPOSCCOD,t.TRCAID,
t.TRMTI,t.TRPRCOD,t.TR87RSPCOD,t.TRMSGRSN,
t.TRREVFLG,t.TRSETTMD,t.TRCURCOD,t.TRAMNT,
t.TRORGAMNT,t.TRCHBLCUR,t.TRFEEAMNT,t.TRCHBLAMNT,
t.TRFRWIIC,t.TRACQIIC,t.TRRECIIC,t.PTRTRCNO,
t.BTRRN, t.TRTRACEDT, t.TRRSPDT, t.TRLDATE,
t.TRLTIME, t.BAID, t.BADATE,t.TRACNTID1,
KWP_Trns_Utils.Get_Acnt_From_TRACNTID(t.TRACNTID2),
t.ATJHNO,t.ATJHDAT, t.TRORGDTELM,t.TRADDATA,
KWP_Trns_Utils.Get_Acnt_From_TRACNTID(t.TRACNTID2),
Trn_ID.Nextval, Trn_diag.Nextval,
1, 1, sttl_cod.Nextval, 1, 1, 1, 1, 1, 1);
end if;
IF batch_size = 10 THEN
begin
COMMIT;
end;
batch_size := 0;
end if;
END loop;
EXCEPTION
WHEN others THEN
ROLLBACK;
END CONVERT_INTO_K2;
The Values expression references t instead of rec the actual name of the cursor record. Try changing those t (only those in the Values expression, not those in the select) into rec.
Also make an alias for KWP_Trns_Utils.Get_Acnt_From_TRACNTID(t.TRPAN) it will be easier. Otherwise you will have to double quote (") and upper case the column name in the Values clause.
Finally and most importantly at first sight your need would be better served by using a insert into … from (select …) syntax. Does this not work?
Failling that, a more robust approach would be to add some form of state column to your source (PSAM952.KCTRNS) table that would indicate that the entry has already been inserted into the destination (PSAM961.KCTRNS2) table. Say 0 for the default value, 1 meaning it will be part of next batch, and 2 to say it has been copied.
In your example, you are doing a mod() of row_num to provide some conditional logic. But you do not have an order-by on you cursor, so the row to which the row_num is applied is not deterministic. Of course one you apply and order-by, then you need to rethink rownum, since that is applied prior to an order-by. So you really need to re-evaluate the logic of what you are trying to do.
Having said that, you can perform conditional logic during the insert as shown below.
insert /*+ APPEND */ into PSAM961.KCTRNS2
select KWP_Trns_Utils.Get_Acnt_From_TRACNTID(t.TRPAN)
, t.TRPRRN
,etc
...
case when xxx=0 then stt1_cod.nextval else 0 end
,1
,1
,etc
from PSAM952.KCTRNS t
where t.trprcod != 47
and rownum < 200;
First, I execute the following SQL statements.
drop table names;
drop table ages;
create table names (id number, name varchar2(20));
insert into names values (1, 'Harry');
insert into names values (2, 'Sally');
insert into names values (3, 'Barry');
create table ages (id number, age number);
insert into ages values (1, 25);
insert into ages values (2, 30);
insert into ages values (3, 35);
select * from names;
select * from ages;
As a result, the following tables are created.
ID NAME
---------- ----------
1 Harry
2 Sally
3 Barry
ID AGE
---------- ----------
1 25
2 30
3 35
Now, I want to update increment the age of Sally by 1, i.e. set it to 31. The following query works fine.
update ages set age = age + 1 where id = (select id from names where name = 'Sally');
select * from ages;
The table now looks like this.
ID AGE
---------- ----------
1 25
2 31
3 35
I want to know if there is a way it can be done by joins. For example, I tried the following queries but they fail.
SQL> update ages set age = age + 1 from ages, names where ages.id = names.id and names.name = 'Sally';
update ages set age = age + 1 from ages, names where ages.id = names.id and names.name = 'Sally'
*
ERROR at line 1:
ORA-00933: SQL command not properly ended
SQL> update ages set age = age + 1 from names join ages on ages.id = names.id where names.name = 'Sally';
update ages set age = age + 1 from names join ages on ages.id = names.id where names.name = 'Sally'
*
ERROR at line 1:
ORA-00933: SQL command not properly ended
The syntax of the UPDATE statement is:
http://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_10007.htm
where dml_table_expression_clause is:
Please pay attention on ( subquery ) part of the above syntax.
The subquery is a feature that allows to perform an update of joins.
In the most simplest form it can be:
UPDATE (
subquery-with-a-join
)
SET cola=colb
Before update a join, you must know restrictions listed here:
https://docs.oracle.com/cd/B28359_01/server.111/b28286/statements_8004.htm
The view must not contain any of the following constructs:
A set operator
A DISTINCT operator
An aggregate or analytic function
A GROUP BY, ORDER BY, MODEL, CONNECT BY, or START WITH clause
A collection expression in a SELECT list
A subquery in a SELECT list
A subquery designated WITH READ ONLY
Joins, with some exceptions, as documented in Oracle Database Administrator's Guide
and also common rules related to updatable views - here (section: Updating a Join View):
http://docs.oracle.com/cd/B19306_01/server.102/b14231/views.htm#sthref3055
All updatable columns of a join view must map to columns of a
key-preserved table. See "Key-Preserved Tables" for a discussion of
key-preserved tables. If the view is defined with the WITH CHECK
OPTION clause, then all join columns and all columns of repeated
tables are not updatable.
We can first create a subquery with a join:
SELECT age
FROM ages a
JOIN names m ON a.id = m.id
WHERE m.name = 'Sally'
This query simply returns the following result:
AGE
----------
30
and now we can try to update our query:
UPDATE (
SELECT age
FROM ages a
JOIN names m ON a.id = m.id
WHERE m.name = 'Sally'
)
SET age = age + 1;
but we get an error:
SQL Error: ORA-01779:cannot modify a column which maps to a non key-preserved table
This error means, that one of the above restriction is not meet (key-preserved table).
However if we add primary keys to our tables:
alter table names add primary key( id );
alter table ages add primary key( id );
then now the update works without any error and a final outcome is:
select * from ages;
ID AGE
---------- ----------
1 25
2 31
3 35
My requirement is to update sequence no of all the rows in table where input_disp_req ='Y' and when updating column input_disp_req Please help me in creating the trigger. I am getting error even though i have used after update.
ORA-04091 (table fce_template is mutating. Trigger/function might not see it)
CREATE OR REPLACE TRIGGER fce_trigger
AFTER UPDATE of input_disp_req ON fce_template
for each row
BEGIN
update fce_template
set pos_clmn = fce_seq.nextval
where region = :new.region
and mode_name = :new.mode_name
and input_disp_req = 'Y';
END;
Please let me know if there is any other way to do this. Please help me. Thanks a lot in advance :)
there are 3 columns input_disp_req, pos_clmn and pos_seq...
column input_disp_req contains value as Y or N (kind of
active/inactive indicator)
column pos_seq maintains sequence no irrespective of Y or N in input_disp_req column
column pos_clmn maintains sequence no based on value of input_disp_req = Y
so my requirement here is that when ever I update value in column input_disp_req to N/Y trigger should
update the sequence values of column pos_clmn for all the matching rows ordered by pos_seq.
input_disp_req=Y means that value is present in incoming data for my requirement.
Let's see if I can make sense of your request and the comments you have made so far...
It seems there already is a column that represents an order for you (your comment to DavidAldridge). Let's say it is called order_col. This makes it possible to retrieve the records in that order:
select *
from fce_template
order by order_col;
You can also get the 'Y' records out in order:
select *
from fce_template
where input_disp_req = 'Y'
order by order_col;
And also for a certain region and mode:
select *
from fce_template
where region = :region
and mode_name = :mode_name
and input_disp_req = 'Y'
order by order_col;
But somehow this seems not enough for you. Do you simply want to apply row numbers (within region and mode)? That can be done with ROW_NUMBER:
select f.*, row_number() over (order by order_col) as pos_clmn
from fce_template f
where region = :region
and mode_name = :mode_name
and input_disp_req = 'Y'
order by order_col;
And for all data:
select
f.*,
row_number() over (partition by input_disp_req, region, mode_name
order by order_col) as pos_clmn
from fce_template f
where input_disp_req = 'Y'
order by order_col;
If this is what you want, you see, you can always create row numbers on the fly. You don't have to store them. It would even be bad to store them, because you would introduce redundance: The order would be given twice, once by order_col, once by pos_clmn. Don't do that.
you can try to use the compound trigger in a manner:
after each row:
get the rowid or the key column of the updated row and put it into a pl/sql collection
after statement:
run through all the rows and update the sequence
it should get rid of the "mutating table" error...
something like that (it may be missing some var declaration):
create or replace trigger your_trigger
for update on your_table
compound trigger
--Trigger level variables so we capture all updates.
type t_rec is record(
flg varchar2(1));
type t_list_tab is table of t_rec index by pls_integer;
l_tab t_list_tab;
after each row is
l_new_the_id := :new.the_id;
l_old_the_id := :old.the_id;
if (not l_tab.exists(l_new_the_id)) then
l_cnt_tab(l_new_the_id).flg := '';
end if;
end after each row;
after statement is
if (l_tab.count != 0) then
for l_idx in l_tab.first .. l_tab.last loop
--your updates here
end loop;
end if;
end after statement;
end;
Probably you just NEED TO use triggers but everyone will just tell you that you shouldn't - triggers are pain in the ass when you try to track the changes or find a bug - probably better would be to use a procedure to update the table and do all the stuff using its logic...
This is no answer. I only want to illustrate what you are doing.
Say you have this table:
input_disp_req region mode_name pos_clmn some_data
Y R1 M1 1 A
Y R1 M1 2 B
Y R1 M1 3 C
N R1 M1 4 D
N R1 M1 5 E
Now let's take this update statement:
update fce_template set input_disp_req = 'Y' where input_disp_req = 'N';
This would update two records, so your trigger gets fired twice. On first execution there will be four records to give the sequence numbers 6 to 9 to. On second execution all five records will be 'Y' and get the sequence numbers 10 to 14. So after your update your records may look like this:
input_disp_req region mode_name pos_clmn some_data
Y R1 M1 14 A
Y R1 M1 10 B
Y R1 M1 12 C
Y R1 M1 13 D
Y R1 M1 11 E
What do you gain by this? It doesn't seem to make sense. It seems that you are using a wrong approch to something you want to achieve.
You can try making the trigger before update:
CREATE OR REPLACE TRIGGER fce_trigger
BEFORE UPDATE of input_disp_req ON fce_template