Statement Triggers on a VIEW in Oracle - oracle

Is it possible in Oracle DB to create Statement trigger (but not Row trigger) on a VIEW?
When I create INSTEAD OF trigger without FOR EACH ROW option on a view, Oracle fires that trigger for each row any way.
For example, the following code:
CREATE TABLE TEST_TABLE (
MY_DATA VARCHAR(30)
);
INSERT INTO TEST_TABLE(MY_DATA) VALUES('one');
INSERT INTO TEST_TABLE(MY_DATA) VALUES('two');
INSERT INTO TEST_TABLE(MY_DATA) VALUES('three');
CREATE OR REPLACE VIEW TEST_VIEW AS
SELECT * FROM TEST_TABLE;
CREATE OR REPLACE TRIGGER TEST_VIEW_TRG1
INSTEAD OF DELETE ON TEST_VIEW
DECLARE
BEGIN
Dbms_Output.Put_Line('STATEMENT TRIGGER.');
END;
/
CREATE OR REPLACE TRIGGER TEST_VIEW_TRG2
INSTEAD OF DELETE ON TEST_VIEW FOR EACH ROW
DECLARE
BEGIN
Dbms_Output.Put_Line('ROW TRIGGER: '||:OLD.MY_DATA);
END;
/
DELETE FROM TEST_VIEW;
Produces the following output:
ROW TRIGGER: one
STATEMENT TRIGGER.
ROW TRIGGER: two
STATEMENT TRIGGER.
ROW TRIGGER: three
STATEMENT TRIGGER.
When I create triggers TEST_VIEW_TRG1 and TEST_VIEW_TRG2 as AFTER on a TEST_TABLE (instead of a TEST_VIEW) the output is as expected:
ROW TRIGGER: one
ROW TRIGGER: two
ROW TRIGGER: three
STATEMENT TRIGGER.
Is there any workaround for this issue?

INSTEAD OF Triggers on views are always row-based, as stated in the Docs: http://download.oracle.com/docs/cd/B19306_01/server.102/b14200/statements_7004.htm#i2235611
FOR EACH ROW
Specify FOR EACH ROW to designate the trigger as a row trigger. Oracle Database fires a row trigger once for each row that is affected by the triggering statement and meets the optional trigger constraint defined in the WHEN condition.
Except for INSTEAD OF triggers, if you omit this clause, then the trigger is a statement trigger. Oracle Database fires a statement trigger only once when the triggering statement is issued if the optional trigger constraint is met.
INSTEAD OF trigger statements are implicitly activated for each row.

Related

DB Insertion Issue on Materialized View in PSQL

I have been working on creating the trigger for one materialized view like this in PSQL using supruser:
returns trigger language plpgsql
as $$
begin
refresh materialized view mat_view;
return null;
end $$;
create trigger refresh_mat_view
after insert or update or delete or truncate
on table1 for each statement
execute procedure refresh_mat_view();
create trigger refresh_mat_view
after insert or update or delete or truncate
on table2 for each statement
execute procedure refresh_mat_view();
After set up the trigger, when others tried to insert records in table1, it showed failed to insert and must be the owner of the mat_view. Is there any way that I can get around this for creating the trigger and also allow others to insert?
https://blog.rustprooflabs.com/2021/07/postgres-permission-mat-view
Issue can be solved here by creating a group of role to be act as an owner or using a cron job

Control a trigger using when clause in Oracle

I am trying to create trigger TRIG_USER in Oracle db. Is there a way to control this trigger using the when clause.I want the trigger to activate only if there a entry in the table trigger_switch
I am looking something like this.
CREATE TRIGGER TRIG_USER
AFTER INSERT OR UPDATE OR DELETE ON USER_TABLE
FOR EACH ROW WHEN (IF EXISTS (SELECT 1 FROM trigger_switch))
BEGIN
[...]
END;

Mutating table exception is not occurring

I have created a trigger and was expecting a mutating table error in below case but didn't get one through normal insert but getting an error while inserting using a query. I am not sure which concept I am missing here.
drop table temp;
create table temp (id number,name varchar2(500),is_active number);
create or replace trigger temp_trg before insert on temp
for each row
declare
v_count number;
begin
select count(1) into v_count from temp;
update temp set is_active=0 where is_active=1 and id=:new.id;
end;
/
select * from temp;
insert into temp values (1,'xyz',1);
insert into temp values (1,'xyz',1);
insert into temp select 1,'xyz',1 from dual;
getting an error while inserting using a query.
Mutating table occurs when we query the table which owns the trigger. Specifically it happens when Oracle can't guarantee the outcome of the query. Now when you insert a single row into the table Oracle can predict the outcome of the query when the FOR EACH ROW trigger fires, because it's a single row.
But with an INSERT FROM query Oracle is confused: should the count be the final figure including all the rows selected by the query or just a rolling count? The answer seems straightforward in this case but it's easy to imagine other queries where the answer is not clear cut. Rather than evaluate each query on its predictability Oracle enforces a plain fiat and hurls ORA-04091 for all query driven inserts.
The restriction on Mutating Tables applies to all triggers that use FOR EACH ROW clause except when either of the following is true:
the trigger fires on BEFORE event, i.e. the data wasn't actually changed
it is known that only one row will be affected - INSERT ... VALUES is the only DML that meets this condition

How do I alter a trigger in Oracle?

I am trying to change the trigger script for my database. My trigger name is ARCH_USER_UPD_TRG and this puts any updates or deletes on the USER table into a Z_USER table
I am dropping a column from the USER table and now need to modify the trigger script to no longer use this column.
How do I modify the PL/SQL script of an oracle trigger?
A trigger is similar to a package or a procedure, so you can simply use
create or replace trigger triggerName
...
declare
...
begin
...
end;
The easy solution would be to Drop and Create the trigger once again with the modified SQL script code.
DROP TRIGGER ARCH_USER_UPD_TRG;
CREATE TRIGGER ARCH_USER_UPD_TRG
//rest of code body

How to prevent an Insert Trigger from being fired when no row is inserted?

I have a TABLE1. On this table I created a trigger : AFTER INSERT OR UPDATE OR DELETE
Now, if I do an insert which doesn't insert anything, the trigger will still be fired :
insert into TABLE1 select * from TABLE1 where 1=0;
This query will insert NO ROWS but yet the trigger is still fired.
Is there a way to avoid this ? Is this normal behavior?
Yes, it is normal behaviour. It can be avoided, though doing so requires having 3 triggers:
A BEFORE trigger to set a package boolean variable to FALSE
A FOR EACH ROW trigger to set the variable to TRUE when a row is inserted
The AFTER trigger you have, where you can now check the value of the variable before carrying out its action.
Sounds like overkill? Maybe it is: what are to trying to achieve with your trigger?
This is the normal behaviour, Oracle is expecting to insert row so it fires both BEFORE INSERT and AFTER INSERT triggers, even if no row is inserted. One of the purpose of the AFTER triggers is to do some action after a statement succeeds. This statement succeeded :)
You could count the number of rows affected with a package variable:
set the mypackage.table1_nb_ins variable to 0 in a BEFORE INSERT trigger
increment the counter in a AFTER INESRT FOR EACH ROW trigger
and then perform your AFTER INSERT logic if your counter is greater than 0
Row Triggers
A row trigger is fired each time the table is affected by the triggering statement. For example, if an UPDATE statement updates multiple rows of a table, a row trigger is fired once for each row affected by the UPDATE statement. If a triggering statement affects no rows, a row trigger is not run.
https://docs.oracle.com/cd/B19306_01/server.102/b14220/triggers.htm

Resources