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

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.

Related

oracle : Trigger not inserting records into table

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;

How to create a trigger that wont hold the entire transaction till it complete?

There is a procedure which have insert operation in middle for a table. The table contain a trigger and because of that the entire transaction getting hold. Is there are anyway to make trigger run on separate session and after insert operation procedure runs without waiting for the trigger to complete.
Both procedure and the trigger are
PRAGMA AUTONOMOUS_TRANSACTION
You could try runnig the trigger part as dbms_job ... as follows:
CREATE OR REPLACE TRIGGER myrigger
AFTER INSERT
ON mytable
REFERENCING NEW AS New OLD AS Old
FOR EACH ROW
DECLARE
l_job number;
begin
dbms_job.submit( l_job, 'MYPACKAGE.MYFUNCTION(''' || :new.myField || ''');' );
END ;
/
If Trigger is based on insert operation, it will hold the current session, until that trigger action is completed. This is required to maintain integrity at database side. It may be possible to design a procedure & trigger in a better way if the requirements are known.

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.

How to update a table using an AFTER INSERT trigger in that same table?

I have 2 triggers on a table A one is BEFORE INSERT and one AFTER INSERT :
CREATE OR REPLACE TRIGGER DEMO_TRG
AFTER INSERT
ON A
REFERENCING NEW AS NEW OLD AS OLD
FOR EACH ROW
declare
PRAGMA AUTONOMOUS_TRANSACTION;
begin
UPDATE A
SET STATUS = 'DONE'
WHERE id_id =:new.p_id;
commit;
END;
/
Why the update command in the after insert doesn't work (the status is not set to DONE)? Did I missed something?
Most likely the issue is due to the AUTONOMOUS_TRANSACTION - when you do that, you're effectively switching to a different transaction, which won't know about your uncommitted transaction (ie. your insert), so there is nothing for it to update.
What you should do is amend the column (:new.status := 'DONE') in your before trigger. Or, even better, avoid triggers and have the logic in a stored procedure and don't allow anyone to directly insert into the table, although I appreciate this may be a big switch if you have lots of applications inserting directly into tables etc

Resources