What do references to OLD evaluate to in the WHEN cause of an Oracle insert trigger? - oracle

When writing a row-level trigger in Oracle, I know that you can use the OLD and NEW pseudo-records to reference the old and new state of the row that fired the trigger. I know that in an INSERT trigger OLD doesn't contain any data, but I'm not sure how this affects the evaluation of a WHEN clause for that trigger. For example, if I have the following trigger:
CREATE OR REPLACE TRIGGER mung_row
BEFORE INSERT OR UPDATE ON some_table
FOR EACH ROW
BEGIN
:NEW.foo = 'some val';
END;
and I want to modify this trigger to only run on an update when foo was previously null, but always run on an insert, I could satisfy the update part of the change by adding a WHERE clause:
CREATE OR REPLACE TRIGGER mung_row
BEFORE INSERT OR UPDATE ON some_table
FOR EACH ROW
WHEN (OLD.foo IS NULL)
BEGIN
:NEW.foo = 'some val';
END;
Will this cause problems in the insert case? What will OLD.foo evaluate to in the INSERT?
I'm aware that I could split the INSERT and UPDATE triggers or use INSERTING/UPDATING/DELETING in the trigger body, but I'd rather not in the case that inspired this question.

When a record is being inserted, every field of OLD will be NULL, including the fields marked as NOT NULL in the table's definition.
For example, suppose your table has a non-nullable column named id:
CREATE TABLE some_table (
id NUMBER NOT NULL,
foo VARCHAR2(100)
)
When a record is inserted into this table, OLD.id will be NULL. However, when a record is updated in this table, OLD.id will not be NULL. Because you only want to change :NEW.foo if a record is being updated, you just have to check to see if OLD.id has a non-null value.
CREATE OR REPLACE TRIGGER mung_row
BEFORE INSERT OR UPDATE ON some_table
FOR EACH ROW
WHEN (OLD.id IS NOT NULL AND OLD.foo IS NULL)
BEGIN
:NEW.foo = 'some val';
END;

Related

Nullify an inserting value in a row trigger

I have a problem with my database trigger in which I'm supposed to nullify the inserted/updated value (RESULTATOBT), although I need the OLD/NEW variables, so I can't apply this solution (updating the table in the table trigger).
The error is a mutating table one; due to modifying a table in a for each row trigger.
Here's the concerned extract of my code:
CREATE OR REPLACE TRIGGER VERIF_RESULTATS
AFTER INSERT OR UPDATE OF RESULTATOBT
ON PARTICIPATION
FOR EACH ROW
FOLLOWS VERIF_PARTICIPATION
WHEN ( NEW.RESULTATOBT IS NOT NULL )
BEGIN
UPDATE PARTICIPATION p
SET RESULTATOBT = NULL
WHERE p.CDPERS = :NEW.CDPERS
AND p.CDCOMPET = :NEW.CDCOMPET;
END;
/
Thank you in advance for your help.
N.B.: I'm a relatively new member, so I might require additional advices/edits if my question doesn't fit the proper format.
Use a before trigger and simply assign NULL:
CREATE OR REPLACE TRIGGER VERIF_RESULTATS
BEFORE INSERT OR UPDATE OF RESULTATOBT
ON PARTICIPATION
FOR EACH ROW
WHEN ( NEW.RESULTATOBT IS NOT NULL )
BEGIN
:new.RESULTATOBT := NULL;
END;
/

how do I create a condition in oracledb?

How do i create a condition on oracle db? I'm new on this db.
I already create table called vehicle_parked, but i want to trigger vehicle number as NULL if parkedOnSite value is 'F' and the structure like this
vehicle_parked
- parked_id number(4) PK
- arrivalTime date
- parkedOnSite varchar(1) // value will be T/F
- vehicle_number varchar(8)
Thanks.
you can easily manipulate your data by creating a DML trigger that fires before your data is inserted into table, and changes data.
CREATE OR REPLACE TRIGGER trg_vehicle_parked
BEFORE INSERT OR UPDATE
ON vehicle_parked
FOR EACH ROW
DECLARE
BEGIN
if :new.parkedOnSite = 'F' then
:new.vehicle_number := null;
end if;
END;
/
In triggers, besides :new, we can use :old pseudo codes. They stand for the values of the columns before(:old) or after(:new) DML statements issued.
Especially for an update or delete trigger you may compare your column's old and new values in a trigger as in the example :
if ( nvl(:old.vehicle_number,0) != nvl(:new.vehicle_number,0) ) then
go_on_with_statement ....

How to create a trigger which fires when any insert or update is done in a table?

I have created the below trigger, but it is not getting fired after i am doing new insert/update in to the mentioned table:
CREATE OR REPLACE TRIGGER ref_upd_user_phi_details
AFTER
INSERT OR UPDATE --of emp_email_address, ssn_nb
ON ref_adp_employees
REFERENCING OLD AS OLD NEW AS NEW
FOR EACH ROW
BEGIN
UPDATE ref_adp_employees
SET emp_email_address = 'QA_' ||emp_email_address,
ssn_nb = nvl2(ssn_nb, NULL, '123-45-6789')
WHERE upper(emp_email_address) NOT LIKE 'QA_%'
AND upper(emp_email_address) LIKE '%#KEENAN.COM';
exception
WHEN others
THEN
NULL;
END;
Can someone please suggest me what i am missing?
As #phonetic_man pointed out, you are hiding any error you get by catching when others and taking no action. Without the exception block you would see that you are causing a mutating table error (ORA-04091), because you are referring to the same table the trigger is against.
If you took out the for each row part to turn it into a statement-level trigger then you would avoid that issue, but now you would have an infinite loop (ORA-00036) - when you try to update the table from within the trigger, that update itself causes the same trigger to fire again; which tries to update the same table yet again, which causes the trigger to fire yet again; etc. until Oracle notices and kills the process.
It would make more sense to use a before-insert row-level trigger to make sure the new values for the row match whatever pattern you are trying to enforce. Maybe something like:
CREATE OR REPLACE TRIGGER ref_upd_user_phi_details
BEFORE INSERT OR UPDATE --of emp_email_address, ssn_nb
ON ref_adp_employees
REFERENCING OLD AS OLD NEW AS NEW
FOR EACH ROW
BEGIN
IF upper(:NEW.emp_email_address) NOT LIKE 'QA_%'
AND upper(:NEW.emp_email_address) LIKE '%#KEENAN.COM'
THEN
:NEW.emp_email_address := 'QA_' || :NEW.emp_email_address;
:NEW.ssn_nb := CASE WHEN :NEW.ssn_nb IS NULL THEN '123-45-6789' END;
END IF;
END;
/
And to see what it does:
insert into ref_adp_employees (emp_id, emp_email_address, ssn_nb) values (1, 'TEST_1', '123-45-6789');
insert into ref_adp_employees (emp_id, emp_email_address, ssn_nb) values (2, 'TEST_1#KEENAN.COM', '123-45-9876');
insert into ref_adp_employees (emp_id, emp_email_address, ssn_nb) values (3, 'QA_TEST_1', null);
select emp_id, emp_email_address, ssn_nb from ref_adp_employees;
EMP_ID EMP_EMAIL_ADDRESS SSN_NB
---------- ------------------------------ -----------
1 TEST_1 123-45-6789
2 QA_TEST_1#KEENAN.COM
3 QA_TEST_1
Not sure if you really intended to replace set SSNs with null, and turn nulls into the fixed value; I suspect you are really trying to replace set values with the fixed string and leaves nulls alone, in which case it would be:
:NEW.ssn_nb := CASE WHEN :NEW.ssn_nb IS NOT NULL THEN '123-45-6789' END;
You might also want to move that outside the IF block, so it's done regardless of the email address; I've replicated what your original code was trying to do but that might not be right.
If you have existing data that you want to modify to match these changes, do a one-off update of the whole table - don't try to do that inside a trigger.
Have you heard of Google? There's a myriad of answers and examples out there for triggers, but some things I see at this time:
1) Change trigger to BEFORE insert or update. Use AFTER when making changes to another table, or to run some subsequent process on the table.
2) Take out the comment to the individual fields being changed or added. That was good AFAIK.
3) In your body of the trigger use WHEN INSERTING and WHEN UPDATING. Or if you are just updating the table, change DDL to BEFORE UPDATE only.
4) In the update, reference with set :new.emp_email_address = 'QA_' || :old.emp_email_address ... and so on and so forth. That's where that old as old and new as new becomes important.
Kindly check if the trigger is valid or not by firing the below query..
SELECT *
FROM ALL_OBJECTS
WHERE OBJECT_NAME = trigger_name
AND OBJECT_TYPE = 'TRIGGER'
AND STATUS <> 'VALID'
The trigger is firing after update..
try before update
CREATE OR REPLACE TRIGGER ref_upd_user_phi_details
Before
INSERT OR UPDATE

Trigger in oracle. Update field when insert or update another field

I need To update the field DATAMARKER"of my table LOG_ALARMA when I have one INSTER or UPDATE of "CONTADOR".
i have this, but return muting error.
create or replace TRIGGER TRIGGER2
AFTER INSERT OR UPDATE OF CONTADOR ON LOG_ALARMA
for each row
BEGIN
UPDATE LOG_ALARMA a
SET a.DATAMARKER=(SYSDATE);
END;
I look another examples and they work but i can't execute this correctly.
IF i comment the line for each row in my trigger body then it is working fine but it UPDATES all the rows in my table.
You do not issue an update SQL statement, because that would again cause the trigger to fire.
Instead, you just set the value :new.DATAMARKER to sysdate, using PL/SQL not SQL.
Make it a BEFORE INSERT OR UPDATE also.
CREATE TABLE Mutating
(
ID1 NUMBER,
DATE1 DATE
)
data present in a table is
ID1 DATE1
1 09/01/2015 14:09:14
1 08/31/2015 14:09:21
2 08/30/2015 14:09:30
Now i want to update the date1 if any update happens on id1 column in that situation i have used trigger look below.
CREATE OR REPLACE TRIGGER Mutating_trg
before INSERT OR DELETE OR UPDATE ON Mutating
referencing old as old new as new
for each row
begin
if updating then
:new.date1:=sysdate;
end if;
end;
then i have issued update statement
update set Mutating id1=6 where trunc(date1)=trunc(sysdate-2)
1 row updated
Now look into results
ID1 DATE1
1 09/01/2015 14:09:14
6 09/02/2015 14:09:14
2 08/30/2015 14:09:30
You should use before statement.

TRIGGER Oracle to prevent updating or inserting

I am having problems with this code below, which is a trigger used in Oracle SQL:
CREATE OR REPLACE TRIGGER TRG_TUTOR_BLOCK
BEFORE INSERT OR UPDATE ON tutors
FOR EACH ROW
DECLARE
BEGIN
IF :new.tutorName = :old.tutorName
THEN
RAISE_APPLICATION_ERROR(-20101, 'A tutor with the same name currently exists.');
ROLLBACK;
END IF;
END;
/
This trigger is used to prevent users from entering the same tutor name at different records.
After I insert two records with the same tutorname, the trigger does not block me from inserting it. Is there anyone can tell me what are the problems with this coding? Here are the sample format and insert values:
INSERT INTO tutors VALUES (tutorID, tutorName tutorPhone, tutorAddress, tutorRoom, loginID);
INSERT INTO tutors VALUES ('13SAS01273', 'Tian Wei Hao', '019-8611123','No91, Jalan Wangsa Mega 2, 53100 KL', 'A302', 'TianWH');
Trigger in Kamil's example will throw ORA-04091, you can see this with your own eyes here. ROLLBACK in a trigger is unnecessary, it runs implicitly when a trigger makes a statement to fail.
You can prohibit any DML on table by altering it with read only clause:
alter table tutors read only;
At last, integrity should be declarated with integrity constraints and not with triggers.
Good luck!
You don't need a trigger for this in Oracle.
You can do it with an "unique index" on the tutorName column (see http://docs.oracle.com/cd/B28359_01/server.111/b28310/indexes003.htm#i1106547).
Note: about your trigger, it fails on checking for another record with the same tutorName because it's not scanning the tutors table for another record with the same tutorName, it's just comparing the tutorName values of the row you are creating (in this case, old.tutorName is just NULL, because the row doesn't exist yet).
Check the case in yours trigger body
IF :new.tutorName = :old.tutorName
It returns true only if 'tutorName' value is the same in new and old record. When you'll trying to updat some value you'll get
IF 'someTutorName' = 'someTutorName'
which will return TRUE.
Inserting row cannot fire this rule because you're trying to compare something like that:
'someTutorName' = NULL
This case always returns FALSE.
Try to use something like that
CREATE OR REPLACE TRIGGER TRG_TUTOR_BLOCK
BEFORE INSERT OR UPDATE ON tutors
FOR EACH ROW
DECLARE
rowsCount INTEGER;
BEGIN
SELECT COUNT(*) FROM tutors WHERE tutorName is :new.tutorName INTO rowsCount;
IF rowsCount > 0
THEN
RAISE_APPLICATION_ERROR(-20101, 'A tutor with the same name currently exists.');
ROLLBACK;
END IF;
END;
/
But the best solution is the one mentioned by friol - use unique index by executing SQL like this
ALTER TABLE tutors
ADD CONSTRAINT UNIQUE_TUTOR_NAME UNIQUE (tutorName);
If you wanna completely ignore recording a row to a table you can follow these steps
rename table to something else and create a view with the same name and create an instead of trigger.
create table usermessages (id number(10) not null)
GO
alter table usermessages rename to xusermessages
GO
create or replace view usermessages as (select * from xusermessages)
GO
create or replace trigger usermessages_instead_of_trg
instead of insert or update on usermessages
for each row
begin
Null ;
end ;
GO
insert into usermessages(123)
Live test available here below
http://sqlfiddle.com/#!4/ad6bc/2

Resources