ORACLE and TRIGGERS (inserted, updated, deleted) - oracle

I would like to use a trigger on a table which will be fired every time a row is inserted, updated, or deleted.
I wrote something like this:
CREATE or REPLACE TRIGGER test001
AFTER INSERT OR DELETE OR UPDATE ON tabletest001
REFERENCING OLD AS old_buffer NEW AS new_buffer
FOR EACH ROW WHEN (new_buffer.field1 = 'HBP00')
and it works.
Since I would like to do the same things if the row is inserted, updated, or deleted, I would like to know what's happening in the trigger.
I think I can manage to find if the row is inserted or updated (I can check the old_buffer with the new_buffer).
How can I know if the row has been deleted?

From Using Triggers:
Detecting the DML Operation That Fired
a Trigger
If more than one type of DML operation
can fire a trigger (for example, ON
INSERT OR DELETE OR UPDATE OF
Emp_tab), the trigger body can use the
conditional predicates INSERTING,
DELETING, and UPDATING to check which
type of statement fire the trigger.
So
IF DELETING THEN ... END IF;
should work for your case.

I've changed my code like this and it works:
CREATE or REPLACE TRIGGER test001
AFTER INSERT OR UPDATE OR DELETE ON tabletest001
REFERENCING OLD AS old_buffer NEW AS new_buffer
FOR EACH ROW WHEN (new_buffer.field1 = 'HBP00' OR old_buffer.field1 = 'HBP00')
DECLARE
Operation NUMBER;
CustomerCode CHAR(10 BYTE);
BEGIN
IF DELETING THEN
Operation := 3;
CustomerCode := :old_buffer.field1;
END IF;
IF INSERTING THEN
Operation := 1;
CustomerCode := :new_buffer.field1;
END IF;
IF UPDATING THEN
Operation := 2;
CustomerCode := :new_buffer.field1;
END IF;
// DO SOMETHING ...
EXCEPTION
WHEN OTHERS THEN ErrorCode := SQLCODE;
END;

The NEW values (or NEW_BUFFER as you have renamed them) are only available when INSERTING and UPDATING. For DELETING you would need to use OLD (OLD_BUFFER). So your trigger would become:
CREATE or REPLACE TRIGGER test001
AFTER INSERT OR DELETE OR UPDATE ON tabletest001
REFERENCING OLD AS old_buffer NEW AS new_buffer
FOR EACH ROW WHEN (new_buffer.field1 = 'HBP00' OR old_buffer.field1 = 'HBP00')
You may need to add logic inside the trigger to cater for code that updates field1 from 'HBP000' to something else.

Separate it into 2 triggers. One for the deletion and one for the insertion\ update.

Related

PL SQL Compund Triggers on Batch Inserts

I've written a compound trigger to fire on inserts. Multiple inserts are batched together and sent to the DB where the compound trigger picks it up. My problem is that i need to perform an update query on the same table for certain inserts depending on the data provided by the query. I can't run a row level action since that would result in a mutating trigger table error (ORA-4091). Best thing i could think of was to have the update query in the before or after statement blocks. i cannot have it on the before statement block since each update is dependent on individual inserts and there's no way of knowing the values before actually reaching that query. so i created a "Type" table and updated it before each row is modified and then later at the after statement block i iterate through the Type table and perform update queries using the data on the table. No matter what i tried the After statement block will only perform update queries for the last insert only.
TYPE apple IS RECORD ( v_size apple_t.size%Type, v_color apple_t.color%Type);
TYPE t_apple IS TABLE OF apple INDEX BY VARCHAR2(20);
BEFORE ROW
t_apple(key).v_size := :New.size;
t_apple(key).v_color := :New.color;
END BEFORE ROW
AFTER STATEMENT
Iterator := t_apple.First;
LOOP EXIT WHEN ITERATOR IS NULL;
UPDATE apple_t SET SIZE = 10
WHERE color = t_apple(Iterator).color;
Iterator := t_apple.Next(Iterator);
END LOOP
END AFTER STATEMENT
This basically is how the trigger is designed. Using a second table is out of the question since trigger cost is a major factor. Any Pointers? Please and Thankyou
I dont fully understand but I think you can get your keys after each row ,then update data in after statament block as follows.
declare
idx number := 1 ;
type array_t is varray(10000) of varchar2(100) ;
colorArr array_t := array_t();
AFTER EACH ROW IS
BEGIN
if inserting then
colorArr (idx) := :new.color;
idx := idx + 1 ;
end if;
END
AFTER EACH ROW;
AFTER STATEMENT IS
BEGIN
for i in 1..sicilNoCol.count
loop
-- update here
end loop;
END AFTER STATEMENT;
or why dont you write a simple before insert trigger that you can manuplate :new.size in it? Does it give table mutable error?

How to Exclude DB Column in a Trigger Function (Oracle)

I have created a very simple trigger in a certain table (e.g. TABLE_TRIGGER) that will call a procedure (handles all the logic). This table has a column (e.g. AUDITID) which I would like to exclude in the trigger function, I mean, if update is only done in AUDITID column, the procedure should not be executed.
Below is the trigger:
CREATE OR REPLACE TRIGGER TABLE_TRIGGER_FUNCTION
AFTER INSERT OR UPDATE
ON TABLE_TRIGGER
REFERENCING NEW AS NEW OLD AS OLD
FOR EACH ROW
BEGIN
-- procedure();
END;
Is there other way to do it, other than the one suggested here: Oracle: excluding updates of one column for firing a trigger
The answer provided here is the one I would like to have Fire trigger on updates (excluding certain fields change) but I am not sure if this is something that can also be done using Oracle PL/SQL syntax.
By the way, I have tried using the following WHEN statement by excluding the AUDITID column but the trigger did not work at all and the procedure was not executed.
WHEN (NEW.FILEID != OLD.FILEID OR
NEW.DESCRIPTION != OLD.DESCRIPTION OR
NEW.IMAGEID != OLD.IMAGEID OR
NEW.STATETYPEID != OLD.STATETYPEID OR
NEW.ACCESSLEVELID != OLD.ACCESSLEVELID OR
NEW.FILETYPEID != OLD.FILETYPEID)
I'm not aware of a way to exclude a column from an update trigger, but you could define a list of columns it would fire on, and list all the columns except the auditid:
CREATE OR REPLACE TRIGGER TABLE_TRIGGER_FUNCTION
AFTER INSERT OR UPDATE
OF FILEID, -- Field list starts here...
DESCRIPTION,
IMAGEID,
STATETYPEID,
ACCESSLEVELID,
ACCESSLEVELID,
FILETYPEID -- ... and ends here
ON TABLE_TRIGGER
REFERENCING NEW AS NEW OLD AS OLD
FOR EACH ROW
BEGIN
-- procedure();
END;
During insets ,all the references to OLD for insert event will result in NULL hence your procedure is not gonna run.
Consider and refer the below code blocks:
I think this is something you are looking for.
create table hr.test(id1 int,id2 int);
create or replace procedure hr.test_proc
as
begin
dbms_output.put_line('trigger');
end;
CREATE OR REPLACE TRIGGER test_trg
AFTER INSERT OR UPDATE
ON hr.test
REFERENCING NEW AS NEW OLD AS OLD
FOR EACH ROW
BEGIN
if updating and :old.id1 !=:new.id1 then
null;--do nothing just as you want
else
hr.test_proc;--call your proc
end if;
END;

Trigger in Oracle: No data found

Please check my code, I seldom use trigger with AFTER clause, thanks so much:
set serveroutput on;
create or replace trigger tvideo_2 after insert on video for each row
declare
pragma autonomous_transaction;
v_title video.title%type; /* Declare a variable to check title that contain '18+' */
begin
select title into v_title from video where video.videoid=:new.videoid;
if v_title like '%18+%' then
update video
set age=18
where videoid=:new.videoid;
dbms_output.put_line('Video '||:new.videoid||' has been updated age to 18+');
else
dbms_output.put_line('Video '||:new.videoid||' is not 18+!');
end if;
end;
/
insert into video values('V5', '18+', 240, 19);
VIDEO properties: (videoid, title, duration, age)
Using pragma autonomous_transaction means that the trigger cannot see the row that has just been inserted and which cause it to be fired.
When you removed the primary key and pre-inserted a record with the same ID, it's that row that will be updated by your trigger, not the new one you are inserting - so it still won't do what you want, and duplicating the PK value isn't sensible anyway.
If you have to do this as an after insert trigger then you can remove the for each row, which avoids the mutating table issue you're presumably trying to avoid, but then you have to query the whole table to find rows to update:
create or replace trigger tvideo_2 after insert on video
begin
update video
set age = 18
where title like '%18+%'
and age != 18;
end;
/
Or if you want to print a message:
create or replace trigger tvideo_2 after insert on video
begin
for rec in (
select videoid, title
from video
where title like '%18+%'
and age != 18
)
loop
update video
set age = 18
where video.videoid = rec.videoid;
dbms_output.put_line('Video '||rec.videoid||' has been updated age to 18+');
end loop;
end;
/
But using dbms_output in a trigger, or really in any stored PL/SQL, isn't a good idea - if the client session that does the insert doesn't look at the output buffer than that message is lost, and it's likely there will nothing (or nobody) to see it anyway.
You could also use an explicit cursor with for update and then update .. where current of.
Either way you're having to query the whole table, which isn't very efficient - you're checking all existing rows every time you insert a new one, instead of just checking that single new value. A trigger isn't really suitable to do that much work - even if it only actually updates a single row (or none!) it has to query everything for every insert. That isn't going to scale very well.
There are other way to avoid the mutating table issue but anything is going to be a hack. The sensible way to do this is with a before-insert row level trigger:
create or replace trigger tvideo_2 before insert on video for each row
begin
if :new.title like '%18+%' then
:new.age := 18;
dbms_output.put_line('Video '||:new.videoid||' has been updated age to 18+');
else
dbms_output.put_line('Video '||:new.videoid||' is not 18+!');
end if;
end;
/
(with the same caveat about using dbms_output) but that seems to be forbidden for some reason.

How to specify what happens for each trigger event in oracle pl sql

Hi I would like to know how I can specify what happens after each trigger event that the trigger monitors, for example:
create or replace trigger Trig_test
AFTER INSERT OR UPDATE
OF name
ON people
AFTER EACH ROW
begin
IF INSERT then do this
ELSIF UPDATE then do this
END IF;
any help would be greatly appreciated, thanks in advance: J.C
You can check it with inserting, updating, or deleting:
create or replace trigger Trig_test
AFTER INSERT OR UPDATE
OF name
ON people
AFTER EACH ROW
begin
if inserting
then
null;
elsif updating
then
null;
end if;
end;

Oracle After Update Trigger error

I want to keep track of changes to one table in another table. What I need is an after update trigger which writes the name of changed column (if multiple columns are changed then there will be multiple inserts to the CHANGES table),the column's old and new values. How do I do that. I tried this but got an error after updating the table.So I'm giving you just the body.
IF :NEW.STAJYEAR!=:OLD.STAJYEAR THEN
INSERT INTO X_STAJ (USERID,EDITDATE,CHANGEDCOLUMN,OLDVALUE,NEWVALUE)
VALUES (:NEW.USERID,SYSDATE,'STAJYEAR',:OLD.STAJYEAR,:NEW.STAJYEAR);
END IF;
the error code is :ORA-04098: trigger 'SYS.TR__TRACK_CHANGES' is invalid and failed re-validation
CREATE OR REPLACE TRIGGER STAJCHANGER.TR_TRACK_CHANGES
AFTER UPDATE
OF STAJYEAR
,STAJMONTH
,STAJDAY
ON STAJCHANGER.STAJ
REFERENCING NEW AS New OLD AS Old
FOR EACH ROW
DECLARE
OLDVALUE NUMBER;
NEWVALUE NUMBER;
COLUMNID NUMBER;
BEGIN
IF :NEW.STAJYEAR!=:OLD.STAJYEAR THEN
INSERT INTO X_STAJ (USERID,EDITDATE,CHANGEDCOLUMN,OLDVALUE,NEWVALUE)
VALUES (:NEW.USERID,SYSDATE,'STAJYEAR',:OLD.STAJYEAR,:NEW.STAJYEAR);
END IF;
IF :NEW.STAJMONTH!=:OLD.STAJMONTH THEN
INSERT INTO X_STAJ (USERID,EDITDATE,CHANGEDCOLUMN,OLDVALUE,NEWVALUE)
VALUES (:NEW.USERID,SYSDATE,'STAJMONTH',:OLD.STAJMONTH,:NEW.STAJMONTH);
END IF;
IF :NEW.STAJDAY!=:OLD.STAJDAY THEN
INSERT INTO X_STAJ (USERID,EDITDATE,CHANGEDCOLUMN,OLDVALUE,NEWVALUE)
VALUES (:NEW.USERID,SYSDATE,'STAJDAY',:OLD.STAJDAY,:NEW.STAJDAY);
END IF;
END TR_TRACK_CHANGES;
/
The error appears to indicates that the trigger owner is SYS, but the creation statement you show explicitly gives the owner as STAJCHANGER.
This makes me wonder, did you accidentally create an (invalid) version of the trigger in SYS at some point, and forget to drop it?
This SQL Plus command will show the error:
SHOW ERROR TRIGGER STAJCHANGER.TR_TRACK_CHANGES

Resources