oracle : Trigger not inserting records into table - oracle

I have a trigger but it's not inserting the records into target table.
create or replace trigger ins_det_trig1
after insert on Table_a
declare
pragma autonomous_transaction;
---
begin
insert into inf_det
select
a.loc_id,
a.genre_id,
to_char(a.emp_date,'yyyy-mm-dd'),
a.san_seq
from Table_b a ,
Table_a b
where
b.emp_date=a.emp_date
and b.genre_id=a.genre_id
and b.san_seq=a.san_seq;
commit;
exception
when others
Rollback;
end;
pls help me on this

First of all the trigger is invalid, so it won't do anything. You are using a variable named v_err that you don't declare.
Then, you are swallowing all exceptions. If something goes wrong, you fill that variable with the error code and then silently end the trigger. Thus you'll never get informed when the trigger fails.
But the main problem is that you are not using the trigger as you should use triggers in Oracle. The trigger is an after row trigger (AFTER INSERT ... FOR EACH ROW) and hence fires once per row on inserts of table_a rows. The values that got inserted in a new row can be accessed with :new, e.g. :new.business_date.
You, however, ignore these values and select from the table instead. But at the moment of your select the table is mutating. Let's say we write an insert statement that inserts two rows. The trigger fires two times. It is left to chance which row gets inserted first. Oracle sees this and when you are inside the trigger and try to select, it tells us that the content of the table is not deterministic, as the other row may already be in the table or not. We get a mutating table exception.
Having said this: It seems you want an after statement trigger. A trigger that fires after the insert of the rows is complete. For this to happen remove the FOR EACH ROW clause and the related REFERENCING clause, too.
create or replace trigger ins_det_trig1
after insert on Table_a
declare
...
begin
...
end;

Related

Precise difference between statement on Row and on Table

What's the difference between these two blocks and when to use the first or the second?
Create OR Replace trigger trig_before_insert before insert on Employee For each Row
Begin
DBMS_OUTPUT.PUT_LINE('Inserting');
END;
And
Create OR Replace trigger trig_before_insert before insert on Employee
Begin
DBMS_OUTPUT.PUT_LINE('Inserting');
END;
If you perform an
INSERT INTO EMPLOYEE
SELECT ...
and that SELECT returns 100 rows so that the INSERT inserts 100 rows, your first trigger will execute 100 times, once for each row. In the same situation, your second trigger will execute only once.
You can use a BEFORE INSERT...FOR EACH ROW trigger to change the values that are being inserted by accessing them via the :NEW variable. E.g.,
:new.column_1 := 'a different value';
You cannot do that in a statement level trigger (which is what your 2nd trigger is).
There are also limitations in row level triggers (which is what your 1st trigger is). In particular, you may not SELECT from the trigger's base table (EMPLOYEES in this case), because that table is said to be "mutating". The exact reasons, as I understand them, go back to the core principles of relational databases -- specifically that the results of a statement (like INSERT INTO...SELECT) should not depend on the order in which the rows are processed. There are workarounds to this limitation, however, which are beyond the scope of your original question, I think.

Before-insert trigger gets 'too many rows' error

I have a trigger:
create or replace trigger trig
before insert on sistem
for each row
declare
v_orta number;
begin
SELECT v_orta INTO :new.orta_qiymet
FROM sistem;
v_orta:=(:new.riyaziyyat+:new.fizika)/2;
insert into sistem(orta_qiymet)
values(v_orta);
end trig;
When I insert a row:
insert into sistem(riyaziyyat,fizika) values(4,4)
I get an error:
Why am I getting that error?
This is fundamentally not understanding how triggers work. You can't generally select from the table the trigger is against, and a before-insert trigger shouldn't not insert into the same table again - as that would just cause the trigger to fire again, infinitely (until Oracle notices and stops it). You aren't even currently using the v_orta value you're attempting to query.
I suspect you think the trigger is instead of your original insert perhaps, and really you want to set the orta_qiymet value in the newly-inserted row automatically based on the other two columns you have supplied. To do that you don't (and can't) select those values; instead you refer to the :NEW pseudorecord as you are already doing, and then set the third column value in that same pseudorow:
create or replace trigger trig
before insert on sistem
for each row
begin
:new.orta_qiymet := (:new.riyaziyyat + :new.fizika)/2;
end trig;
/
There is a lot of information in the documentation; this is similar to one of the examples.

Update table column on update related table column in pl sql

I have a database that among the others contains these two tables:
NARUDZBENICA(**SIFANAR**,DATUM,NAZIV,*SIFRADOB,SIFRAKATALOGA,SIFRAZAP,SIFRANACISP*)
DOBAVLJAC(**SIFRADOB**,NAZIV,MAIL,TELEFON,FAKS)
I need a statement trigger to update column 'naziv' in all rows in table NARUDZBENICA where SIFRADOB starts with '0' when I change column 'naziv' on DOBAVLJAC where SIFRADOB is a primary key.
This is what I came up with:
CREATE OR REPLACE TRIGGER "STATEMENT_DOB"
AFTER UPDATE OF NAZIV ON DOBAVLJAC
BEGIN
EXECUTE IMMEDIATE 'ALTER TRIGGER UPDATE_NAR_FRB DISABLE';
UPDATE NARUDZBENICA
SET NAZIV = (SELECT :OLD.NAZIV FROM DOBAVLJAC)
WHERE ROWNUM > 1 AND SIFRADOB = '%0';
EXECUTE IMMEDIATE 'ALTER TRIGGER UPDATE_NAR_FRB ENABLE';
END;
I don't know what you are trying to do disabling and enabling one trigger in another. Otherwise I can see pretty much what you're trying to do, I just don't understand why.
In plain language: when the field NAZIV in table DOBAVLJAC is updated, the old value of the field is saved to the same field in table NARUDZBENICA where the field SIFRADOB begins with the character '0'.
create or replace trigger STATEMENT_DOB
after update of NAZIV on DOBAVLJAC
begin
update NARUDZBENICA
set NAZIV = :old.NAZIV
where SIFRADOB like '0%';
end;
It just occurred to me why you may be disabling the other trigger. It is an update trigger on the other table that watches for that same field to, in turn, propagate the change to DOBAVLJAC. This would create an endless loop of updates. (It would also means you probably should be using the :new.NAZIV value rather than :old.NAZIV.)
There are several tricks to solve that problem. The more involved one is to rename both tables and create views with the original table names. The Instead Of trigger on each view updates the NAZIV changes to both tables. No looping.
That is a rather involved solution. A simpler one (involving fewer object changes) is to create a flag column in both tables. The value of this column is always NULL. When the trigger executes (and it will have to be the before trigger), it checks the NEW value of the flag column. If it is still null, that means this is the first Update so sends an update to the other table. That update sets the NAZIV value to the new value and the flag field to any non-null value. The non-null value tells the other trigger that this is a propagation update so ends the propagation. It changes the :new.flag value to null (you never actually change the contents of the flag field in the table) and just allows the update. This logic would be the same in the trigger on both tables.
This is made a little easier with Oracle 12c and invisible columns. It just allows you to hide the flag field from normal view so people aren't always coming around and asking what it is for.
In reading over my description, I don't think I made my point clearly, especially for non-native English speakers. So here is the trigger code:
create or replace trigger STATEMENT_DOB -- UPDATE_NAR_FRB
before update of NAZIV, Flag on DOBAVLJAC -- NARUDZBENICA
begin
if updating( Flag ) and not updating( NAZIV ) then
-- Someone playing around updating Flag only. Don't allow.
:new.FLAG := null;
elsif :new.FLAG is null then
-- Original Update. Propagate to other table
update NARUDZBENICA -- DOBAVLJAC
set NAZIV = :new.NAZIV,
Flag = 1
where SIFRADOB like '0%';
else
-- This was propagation from other table. Just allow the update of NAZIV
-- but first reset the flag...
:new.FLAG := null;
end if;
end;
That is not quite production-level coding, but I hope it illustrates the idea.
:new or :old identifiers can only be used in row-level triggers. For statement level trigger use of :new and :old identifiers is forbidden.
Also you cannot commit in a trigger. So incase you want to do any DML operation you need to use an autonomous transaction. See below how you can do it.
This trigger will be fired for any change in column value of ID in table A_TABLE.
The new value will be captured and passed on to the anonymous transaction.
CREATE OR REPLACE TRIGGER STATEMENT_DOB
AFTER UPDATE OF ID ON A_TABLE
for each row
BEGIN
proc_upd_tb(:new.id);
END;
Autonomous Transaction:
Data can be updated here and committed as shown.
create or replace procedure proc_upd_tb(id number)
as
PRAGMA AUTONOMOUS_TRANSACTION;
begin
UPDATE AA
SET A = id;
where <condition> ;
commit;
end;

Is there any data dictionary object in oracle to record the transaction details of triggers?

I have created trigger TEST_TRIG as below:
CREATE TRIGGER TEST_TRIG
AFTER INSERT ON TEST_TABLE
FOR EACH ROW
DECLARE
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
TEST_PROC();
END;
Procedure TEST_PROC code:
create or replace
PROCEDURE TEST_PROC
AS
BEGIN
EXECUTE IMMEDIATE 'truncate table TEST_FINAL';
INSERT INTO TEST_FINAL select * from TEST_TABLE;
commit;
END;
Initially, I disabled TRIGGER TEST_TRIG and inserted a record into TEST_TABLE and executed PROCEDURE TEST_PROC manually.
Output: I was able to fetch the same record what i inserted into TEST_TABLE from TEST_FINAL.
I flushed those records from both table and enabled the trigger TEST_TRIG.
Now when i inserts and commits the record in TEST_TABLE, I didn't found the record in TEST_FINAL table... I haven't received any error message also!!!
So I want to know whether trigger got fired or not?
I don't think you have fully grasped the implications of AUTONOMOUS_TRANSACTION. Effectively it means the code bounded by the pragma runs in a separate session . So, because of Oracle's read consistent isolation level, the autonomous transaction cannot see any of the data changes generated by the main transaction.
Thus, if TEST_TABLE is empty when you start the trigger will insert no rows into TEST_FINAL, regardless of how many rows you're inserting right now.
So: don't flush both tables. Insert some rows into TEST_TABLE and commit. TEST_FINAL will still be empty. Insert some more rows into TEST_TABLE and, lo! the first set of rows will appear in TEST_FINAL.
Obviously this is not the result you want. So you need to revisit your logic. It really doesn't make sense to truncate TEST_FINAL every time and definitely not FOR EACH ROW. That is Teh Suck! as far as performance goes. Likewise and for the same reason it doesn't make sense to populate the target table with INSERT ... SELECT .
Discarding the TRUNCATE means you don't need the pragma and everything becomes much simpler,
If you want to keep a history of the affected rows use something like this instead:
CREATE TRIGGER TEST_TRIG
AFTER INSERT ON TEST_TABLE
FOR EACH ROW
BEGIN
insert into test_final (col1, col2)
values (:new.col1, :new.col2);
END;
You'll need to change the exact code to fit your exact requirements.

Mutating error on after insert trigger

The below code is giving a mutating error.
Can any1 pls help in solving this.
CREATE OR REPLACE TRIGGER aso_quote_cuhk_trigger
BEFORE INSERT
ON aso.aso_quote_headers_all
FOR EACH ROW
BEGIN
UPDATE aso.aso_quote_headers_all
SET quote_expiration_date=sysdate+90
where quote_header_id=:new.quote_header_id;
END;
/
In oracle there are two levels of triggers: row level and table level.
Row level triggers are executed for each row. Table level triggers executed per statement, even if a statement changed more then one row.
In a row level trigger, you cannot select/update the table itself that has the trigger: you will get a mutating error.
In this case, there is no need for an UPDATE statement. Just try this:
CREATE OR REPLACE TRIGGER aso_quote_cuhk_trigger
BEFORE INSERT
ON aso.aso_quote_headers_all
FOR EACH ROW
BEGIN
:new.quote_expiration_date=sysdate+90;
END;
/
EDIT Rajesh mentioned it is possible, that before inserting a new row, OP wants to update all other records in the aso_quote_headers_all table.
Well, this is feasible, but it's a little tricky. To do this properly, you will need
A pl/sql package and a variable in the package header that is modified by the triggers. This variable could be a list holding the IDs of newly inserted records. Row level after insert trigger would add a new ID to the list. The content of this package variable will be different for each different session, so let's call this variable session_variable.
Row level after insert trigger, that would add new ID to the session_variable.
Table level after insert trigger that would get IDs from the session_variable, process the ID and then remove it from the session_variable. This trigger could execute necessary selects/updates on the aso_quote_headers_all. After a newly inserted ID is processed, this trigger should make sure it gets removed from the session_variable.
I realise you must have resolved your issue by now. However I am adding this answer below to help anyone else facing similar problem as you and I faced.
I recently encountered mutating table (ORA-04091: table XXXX is mutating, trigger/function may not see it) issue and after searching around realised the Compound Triggers feature available in 11g. If you're on 11g following compound trigger would have solved your issue.
CREATE OR REPLACE TRIGGER aso_quote_cuhk_trigger
FOR INSERT ON aso.aso_quote_headers_all
COMPOUND TRIGGER
row_id rowid;
AFTER EACH ROW IS
BEGIN
row_id := :new.rowid;
END AFTER EACH ROW;
AFTER STATEMENT IS
BEGIN
UPDATE aso.aso_quote_headers_all
SET quote_expiration_date = sysdate+90
WHERE rowid = row_id;
END AFTER STATEMENT;
END aso_quote_cuhk_trigger;
/
A word about how it works. This compound trigger fires 2 events :
First is AFTER EACH ROW where we capture the rowid of newly inserted row
Next is AFTER STATEMENT where we update the table using rowid (captured during first event) in the WHERE clause.
A useful link if you want to read more about Compound Triggers.

Resources