Reputation: 374
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...
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.
Upvotes: 0
Views: 1251
Reputation: 94913
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.
Upvotes: 2
Reputation: 94913
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.
Upvotes: 1
Reputation: 87
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...
Upvotes: 2
Reputation: 668
You can try making the trigger before update:
CREATE OR REPLACE TRIGGER fce_trigger
BEFORE UPDATE of input_disp_req ON fce_template
Upvotes: 0