Why do I keep getting this error when creating a trigger? - oracle

I am creating a trigger that checks if the Majorcode column is null BEFORE INSERT OR UPDATE and if it is it enters the default value "100"
My code:
CREATE TRIGGER MUmajorcode_changes
BEFORE INSERT OR UPDATE ON M_Students
FOR EACH ROW
BEGIN
IF NEW.Majorcode = null THEN
SET NEW.Majorcode = '100';
END IF;
I get the following message after running it
Warning: Trigger created with compilation errors.
Then when I actually insert a new value with blank major code.
I receive the following message/error:
ERROR at line 1:
ORA-04098: trigger 'SCOTT.MUMAJORCODE_CHANGES' is invalid and failed re-validation.
This is how I setup the table initially:
CREATE TABLE M_Students
(
ID CHAR(3) PRIMARY KEY,
FName VARCHAR2(25),
LName VARCHAR2(25),
Status VARCHAR2(25),
Majorcode CHAR(3) REFERENCES Departments(DeptCode),
GPA NUMBER (4,2),
AdmittedDate DATE
);
Cannot figure out where I am going wrong?

When you get the message "created with compilation errors" it means your script is invalid, it has compile time errors. If your IDE does not show them you to you then if you are using sqlplus then run "show errors" if you are using an IDE run the query "select * from user_errors" (and get a new IDE). Either will show the errors in the script, they must be corrected; your script will not run until they are.
The suggestions of setting a DEFAULT value in the table definition has merit but will fail on a couple conditions. When Inserting the default will be taken when the column name is not mentioned in column list. (Note Omitting the column list is implicitly mentioning all columns so the default would not be set.) Nor would it handle update condition.
Your trigger itself can be reduced to a single statement with the coalesce function:
create or replace trigger mumajorcode_changes
before insert or update of mumajorcode
on m_students
for each row
begin
:new.majorcode := coalesce(:new.majorcode, '100');
end;

You have some syntax errors:
CREATE OR REPLACE TRIGGER MUmajorcode_changes /* better use REPLACE to avoid dropping the trigger */
BEFORE INSERT OR UPDATE
ON M_Students
FOR EACH ROW
BEGIN
IF :new.Majorcode IS NULL /* :new, with the colon (:) */
THEN
:new.Majorcode := '100'; /* :new, and no SET */
END IF;
END; /* a missing END */
Also, never check null values with =, but always use is [not] null.

There are some obvious syntax errors such as wrong assignment, missing or superfluous keywords, but you don't need to create a database trigger to handle this operation. Just modify the related columns property by
ALTER TABLE M_Students
MODIFY Majorcode DEFAULT '100' NOT NULL
Through this property, Majorcode column is already populated by the value '100' during the insertion automatically, and updating that column to NULL won't be possible because of the NOT NULL constraint.
Demo

Related

Trying to delete a row based upon condition defined in my trigger (SQL)

I am trying to create a row level trigger to delete a row if a value in the row is being made NULL. My business parameters state that if a value is being made null, then the row must be deleted. Also, I cannot use a global variable.
BEGIN
IF :NEW.EXHIBIT_ID IS NULL THEN
DELETE SHOWING
WHERE EXHIBIT_ID = :OLD.EXHIBIT_ID;
END IF;
I get the following errors:
ORA-04091: table ISA722.SHOWING is mutating, trigger/function may not see it
ORA-06512: at "ISA722.TRG_EXPAINT", line 7
ORA-04088: error during execution of trigger 'ISA722.TRG_EXPAINT'
When executing this query:
UPDATE SHOWING
SET EXHIBIT_ID = NULL
WHERE PAINT_ID = 5104
As already indicated this is a terrible idea/design. Triggers are very poor methods for enforcing business rules. These should be enforced in the application or better (IMO) by a stored procedure called by the application. In this case not only is it a bad idea, but it cannot be implemented as desired. Within a trigger Oracle does not permit accessing the table the trigger fired was fired on. That is what mutating indicates. Think of trying to debug this or resolve a problem a week later. Nevertheless this non-sense can be accomplished by creating view and processing against it instead of the table.
-- setup
create table showing (exhibit_id integer, exhibit_name varchar2(50));
create view show as select * from showing;
-- trigger on VIEW
create or replace trigger show_iiur
instead of insert or update on show
for each row
begin
merge into showing
using (select :new.exhibit_id new_eid
, :old.exhibit_id old_eid
, :new.exhibit_name new_ename
from dual
) on (exhibit_id = old_eid)
when matched then
update set exhibit_name = new_ename
delete where new_eid is null
when not matched then
insert (exhibit_id, exhibit_name)
values (:new.exhibit_id, :new.exhibit_name);
end ;
-- test data
insert into show(exhibit_id, exhibit_name)
select 1,'abc' from dual union all
select 2,'def' from dual union all
select 3,'ghi' from dual;
-- 3 rows inserted
select * from show;
--- test
update show
set exhibit_name = 'XyZ'
where exhibit_id = 3;
-- 1 row updated
-- Now for the requested action. Turn the UPDATE into a DELETE
update show
set exhibit_id = null
where exhibit_name = 'def';
-- 1 row updated
select * from show;
-- table and view are the same (expect o rows)
select * from show MINUS select * from showing
UNION ALL
select * from showing MINUS select * from show;
Again this is a bad option yet you can do. But just because you can doesn't mean you should. Or that you'll be happy with the result. Good Luck.
You have written a trigger that fires after or before a row change. This is in the middle of an execution. You cannot delete a row from the same table in that moment.
So you must write an after statement trigger instead that only fires when the whole statement has run.
create or replace trigger mytrigger
after update of exhibit_id on showing
begin
delete from showing where exhibit_id is null;
end mytrigger;
Demo: https://dbfiddle.uk/?rdbms=oracle_18&fiddle=dd5ade700d49daf14f4cdc71aed48e17
What you can do is create an extra column like is_to_be_deleted in the same table, and do this:
UPDATE SHOWING
SET EXHIBIT_ID = NULL, is_to_be_deleted = 'Y'
WHERE PAINT_ID = 5104;
You can use this parameter to implement your business logic of not showing the null details.
And later you can schedule a batch delete on that table to clean up these rows (or maybe archive it).
Benefit: you can avoid an extra unnecessary trigger on that table.
Nobody, will suggest you to use trigger to do this type of delete as it is expensive.

Modify column value Oracle

Hello I have made a Trigger it compiles
CREATE OR REPLACE TRIGGER livraisonfinie
BEFORE UPDATE ON Expedition
FOR EACH ROW
DECLARE
BEGIN
IF :NEW.date_livraison <> TO_DATE('3000/01/01 00:00:00', 'yyyy/mm/dd hh24:mi:ss')
THEN
INSERT INTO Commande (etat) VALUES ('livree');
DELETE FROM cmdalivrer CMD WHERE :NEW.numero_commande=CMD.id_cmd WHERE :NEW.numero_commande= Commande.numero_commande;;
END IF ;
END;
/
When I try to update a value of expedition table I get this error
ORA-01400: cannot insert NULL into ("HAMZA"."COMMANDE"."NUMERO_COMMANDE")
ORA-06512: at "HAMZA.LIVRAISONFINIE", line 10
ORA-04088: error during execution of trigger 'HAMZA.LIVRAISONFINIE'
And I have used this update request but not sure if I have to use INSERT INTO or UPDATE and how corretly even after reading many manuals.
Here's the table
Expedition(Id_Expedition ,#id_chauffeur,#Immatriculation, #Id_Itineraire,Date_Deb_Expedition , Date_Livraison)
Commande (numero_commande,Date_commande,adresse_livraison,id_part,ville_livraison,code_postal_livraison,etat,id_expedition)
EDIT: I solved it by adding "where :NEW.numero_commande= Commande.numero_commande;" to my trigger.
Thanks
There is NOT NULL constraint on column NUMERO_COMMANDE in table COMMANDE.
More on this constraint you can find here:
http://www.w3schools.com/sql/sql_notnull.asp
By default, a table column can hold NULL values.
SQL NOT NULL Constraint
The NOT NULL constraint enforces a column to
NOT accept NULL values.
The NOT NULL constraint enforces a field to always contain a value.
This means that you cannot insert a new record, or update a record
without adding a value to this field.
Either provide a value for this field in your trigger:
INSERT INTO Commande (etat,NUMERO_COMMANDE)
VALUES ('livree', some-value-for-numero-commande-column);
or remove NOT NULL constraint on this column from the table.

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

check constraint check the column twice

What is wrong in this error ?
alter table INFO add constraint chk_app check
(CASE WHEN app IS NULL THEN app = 'A');
If its because app = 'A' I am calling it twice then how to have a check constraint to check if app is null then it should have value A
If you want to set the column app to 'A' whenever a row is inserted with a NULL value, I'd use a DEFAULT construct:
ALTER TABLE info MODIFY (app VARCHAR2(xxx) DEFAULT 'A');
If no value is specified during an INSERT, an 'A' is inserted into column app. Of course, a user can override this behaviour by explicitly inserting a NULL value. This can be prevented by a NOT NULL constraint:
ALTER TABLE info MODIFY (app VARCHAR2(xxx) DEFAULT 'A' NOT NULL);
From Oracle version 12c on, you can specify the required behaviour more clearly:
ALTER TABLE info MODIFY (app VARCHAR2(xxx) DEFAULT ON NULL 'A' NOT NULL);
This is bacause check constraint only checks the value, it is not able to change it.
You can simply add default value to app column or write before insert or update trigger like this
create or replace trigger info_briu
before insert or update of info
for each row
begin
if :new.app is null then
:new.app := 'A';
end if;
end;

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

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;

Resources