I'm creating a PL/SQL Trigger that gives me an error if the date_fin < date_begin. (Im new to triggers.)
My problem is that when testing Im getting ORA-01403: no Data found. Id say the trigger has found no data to analyze but my table contains values.
Does my error come from my trigger format?
Here is my code:
--TRIGGER1
CREATE OR REPLACE TRIGGER verifDate
BEFORE INSERT OR UPDATE ON PROJET FOR EACH ROW
DECLARE date_debut projet.dateDebut%TYPE;
date_fin projet.dateFin%TYPE;
pragma autonomous_transaction;
BEGIN
Select projet.dateDebut into date_debut from projet where date_debut=:new.dateDebut;
Select projet.dateFin into date_fin from projet where date_fin=:new.datefin;
IF date_fin < date_debut
THEN
RAISE_APPLICATION_ERROR(-20100, 'Mauvaise date') ;
END IF;
END;
Tests:
--TEST1.1
INSERT INTO PROJET (idProjet, idClient, nomProjet, dateDebut, dateFin, idResponsable)
VALUES (4,355,'Projet Test', '01/12/2020', '30/12/2019', 3851);
--TEST1.2
UPDATE PROJET
SET dateFIN = '01/09/2003' , dateDebut = '31/12/2019'
WHERE Projet.idProjet=2;
Yes, the error comes from the trigger because it is a BEFORE INSERT trigger so the row does not yet exist in the table but you are trying to use SELECT to get data matching the row and it fails with a NO_DATA_FOUND exception.
Instead, you should use the :NEW (and, if you need it, :OLD) record:
CREATE OR REPLACE TRIGGER verifDate
BEFORE INSERT OR UPDATE ON PROJET FOR EACH ROW
BEGIN
IF :NEW.dateFIN < :NEW.dateDebut THEN
RAISE_APPLICATION_ERROR(-20100, 'Mauvaise date') ;
END IF;
END verifDate;
/
However, this should be done using a CHECK constraint:
ALTER TABLE projet ADD CONSTRAINT projet__datedebut_lte_datefin__chk
CHECK (dateDebut < dateFin);
db<>fiddle here
Related
I try to create a custom table, which is created properly. The last 2 fields are matching the type from Orders table. The trigger's idea is when a certain date field is changed to INSERT this into the new table. The trigger is created in Oracle correctly, but once the field in question changes I get ORA errors (below) and cannot find out why they appear.
ORA-06519: active autonomous transaction detected and rolled back
ORA-06512: at "shc.trigger_table_1", line 20
ORA-04088: error during execution of trigger 'shc.trigger_table_1'
I tried changing the datatypes, tried introducing an EXCEPTION, tried removing the :old.info23 check.
Here is the table I am creating:
CREATE TABLE table1(
id number generated by default as identity,
table_name varchar2(20),
field_changed varchar2(20),
old_value date,
new_value date,
changed_by varchar(20),
date_of_change date,
orderRef varchar(50),
Ref varchar2(256));
Here is the problematic trigger:
CREATE OR REPLACE TRIGGER trigger_table_1
AFTER UPDATE ON consignment
FOR EACH ROW
DECLARE
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
DECLARE
my_AEXTERNAUFTRAGSNR varchar2(50);
my_AUFTRAGSREFERENZ2 varchar2(256);
BEGIN
IF :OLD.ANKUNFTBELDATUMVON <> :NEW.ANKUNFTBELDATUMVON AND :old.info23='I' THEN
Select AEXTERNAUFTRAGSNR, AUFTRAGSREFERENZ2
Into my_AEXTERNAUFTRAGSNR, my_AUFTRAGSREFERENZ2
From orders
Where orders.nr = :old.ordernr;
insert into table1(table_name, field_changed, old_value, new_value, changed_by, date_of_change, orderRef, Ref)
values ('consignment', 'ANKUNFTBELDATUMVON', :OLD.ANKUNFTBELDATUMVON, :NEW.ANKUNFTBELDATUMVON, sys_context('userenv','OS_USER'), SYSDATE, my_AEXTERNAUFTRAGSNR, my_AUFTRAGSREFERENZ2);
END IF;
END;
END;
The immediate problem is that you don't commit your autonomous transaction. If you change it to commit at the end:
...
END IF;
END;
commit;
END;
/
then it will work - fiddle. (You don't need the nested block, but it doesn't stop it working... and you could do insert ... select ... to avoid needing any local variables...)
But as you can see from the db<>fiddle result, if the update on consignment is rolled back then the insert into table1 is retained, since that was committed independently.
If you remove the PRAGMA AUTONOMOUS_TRANSACTION; instead then that won't happen. Then you won't need to, and indeed can't, commit within the trigger. You would just need:
CREATE OR REPLACE TRIGGER trigger_table_1
AFTER UPDATE ON consignment
FOR EACH ROW
DECLARE
my_AEXTERNAUFTRAGSNR varchar2(50);
my_AUFTRAGSREFERENZ2 varchar2(256);
BEGIN
IF :OLD.ANKUNFTBELDATUMVON <> :NEW.ANKUNFTBELDATUMVON AND :old.info23='I' THEN
Select AEXTERNAUFTRAGSNR, AUFTRAGSREFERENZ2
Into my_AEXTERNAUFTRAGSNR, my_AUFTRAGSREFERENZ2
From orders
Where orders.nr = :old.ordernr;
insert into table1(table_name, field_changed, old_value, new_value, changed_by, date_of_change, orderRef, Ref)
values ('consignment', 'ANKUNFTBELDATUMVON', :OLD.ANKUNFTBELDATUMVON, :NEW.ANKUNFTBELDATUMVON, sys_context('userenv','OS_USER'), SYSDATE, my_AEXTERNAUFTRAGSNR, my_AUFTRAGSREFERENZ2);
END IF;
END;
/
or with insert ... select ...:
CREATE OR REPLACE TRIGGER trigger_table_1
AFTER UPDATE ON consignment
FOR EACH ROW
BEGIN
IF :OLD.ANKUNFTBELDATUMVON <> :NEW.ANKUNFTBELDATUMVON AND :old.info23='I' THEN
insert into table1(table_name, field_changed,
old_value, new_value,
changed_by, date_of_change,
orderRef, Ref)
select 'consignment', 'ANKUNFTBELDATUMVON',
:OLD.ANKUNFTBELDATUMVON, :NEW.ANKUNFTBELDATUMVON,
sys_context('userenv','OS_USER'), SYSDATE,
o.AEXTERNAUFTRAGSNR, o.AUFTRAGSREFERENZ2
from orders o
where o.nr = :old.ordernr;
END IF;
END;
/
fiddle
Incidentally, if you only want the trigger to fire if a specific column (or columns) was included in an update statement then you could do:
AFTER UPDATE OF ANKUNFTBELDATUMVON ON consignment
but you would still need to check it had actually changed. Your current check doesn't cover ANKUNFTBELDATUMVON being update to or from null, which is only potentially an issue if the column is nullable.
I created table to store informations about time of any update, insert or delete data in one of tables.
CREATE TABLE dept_changes ( data DATE, action VARCHAR2(16) );
Now I want to create trigger inputing the data to table:
CREATE OR REPLACE TRIGGER dept_changes_trig AFTER UPDATE OR INSERT OR DELETE ON departments
DECLARE
action VARCHAR2(16);
BEGIN
IF UPDATING THEN
action:='upd';
END IF;
IF INSERTING THEN
action:='ins';
END IF;
IF DELETING THEN
action:='del';
END IF;
INSERT INTO DEPT_CHANGES (SYSDATE, action);
END;
I got 2 errors in line 12 (END IF of DELETING condition statement).
Error(12,5): PL/SQL: SQL Statement ignored
Error(12,46): PL/SQL: ORA-00926: missing VALUES keyword
I don't understand, what VALUES am I missing? What the trigger needs to work properly?
Use the values keyword
INSERT INTO DEPT_CHANGES VALUES (SYSDATE, action);
Insert Examples
I've been working around this trigger and when I run the script it tells me the previous error message. I can't seem to figure out why it won't compile correctly, every pl/sql trigger tutorial seems to have the structure my trigger has. Code is the following:
create
or replace trigger new_artist before insert
on
Artist referencing new as nvartist declare counter number;
begin select
count( * ) into
counter
from
Performer
where
Stage_name = nvartist.Stage_name;
if counter > 0 then signal sqlstate '45000';
else insert
into
Artist
values(
nvartist.Stage_name,
nvartist.Name
);
insert
into
Performer
values(nvartist.Stage_name);
end if;
end;
It checks if the new artist already exists in its supertype (Performer), if it does exist it gives an error if it doesn't it inserts both into artist(Stage_name varchar2, Name varchar2) and Performer(Stage_name). Another subtype of Performer (and sibling to Artist) is Band(Stage_name), which in turn has a relationship with Artist. Why does the compiler yell at me for this trigger?
Thanks in advance
You may want to try this variant (I slightly modified names of your tables).
Creating tables with sample data:
CREATE table test_artist(
stage_name varchar2(100)
, name varchar2(100)
);
create table test_performer(
stage_name varchar2(100)
);
/*inserting test performer on which trigger will rise an error*/
insert into test_performer
select 'performer_1' from dual;
Creating trigger:
create or replace trigger new_artist
before insert
on TEST_ARTIST
referencing new as nvartist
for each row
declare
counter number;
begin
select count(*)
into counter
from test_performer
where Stage_name = :nvartist.Stage_name;
if counter > 0 then
--signal sqlstate '45000' ;
raise_application_error( -20001, 'No insertion with existing Performer');
else
/*you cant update the same table, in other case you'll get
ora-04091 table mutating error.
But nevertheless this values will be inserted by sql triggered this trigger.*/
--insert into test_artist values(:nvartist.Stage_name, :nvartist.Name);
insert into test_performer values(:nvartist.Stage_name);
end if;
end new_artist;
After that this insert will work, cause the is no 'performer_2' in 'test_performer' table:
insert into test_artist
select 'performer_2', 'name_2' from dual;
And this will fail:
insert into test_artist
select 'performer_1', 'name_1' from dual;
I am trying to create a trigger to check the month before inserting to the database. the followingg code was triedbut showing a complationng error as Warning: Trigger created with compilation errors.
this is the code
CREATE OR REPLACE TRIGGER tr_july
BEFORE INSERT
ON TBL_EVENT
BEGIN
SELECT EXTRACT(month FROM EVN_DATE) FROM TBL_EVENT;
IF EXTRACT (month from EVN_DATE) == 7 THEN
RAISE_APPLICATION_ERROR(-20110, 'NOT ALLOWED TO INSERT RECORDS DURING JULY');
END IF;
END;
/
I think you are looking for something like this:
CREATE OR REPLACE TRIGGER tr_july
BEFORE INSERT ON TBL_EVENT
BEGIN
IF EXTRACT (month from :new.EVN_DATE) = 7 THEN
RAISE_APPLICATION_ERROR(-20110, 'NOT ALLOWED TO INSERT RECORDS DURING JULY');
END IF;
END; /
Study the documentation, a section "Accessing Column Values in Row Triggers"
https://docs.oracle.com/cd/B19306_01/appdev.102/b14251/adfns_triggers.htm
to learn how to access columns of the current row within the trigger body.
In short: You need to use "correlation names" named NEW and OLD
Tip: run SET DEFINE OFF; before compiling the trigger to avoid bind variable substitution (variables prepended by a colon :).
Use it as:
CREATE OR REPLACE TRIGGER tr_july
BEFORE INSERT
ON TBL_EVENT
Declare
E_date date;
BEGIN
SELECT EVN_DATE into E_date FROM TBL_EVENT;
IF EXTRACT (month from E_date) = 7 THEN
RAISE_APPLICATION_ERROR(-20110, 'NOT ALLOWED TO INSERT RECORDS DURING JULY');
END IF;
END;
Note: Oracle if clause require = only once
There are multiple isues with this code.
First it is a before insert trigger, so the value you want to check is not yet in the table. You can't find it with a select.
Triggers are pl/sql code. Any value you select within a pl/sql procedure you have to 'select aaa INTO bbb from xxx; and bbb must be declared before your BEGIN.
In Oracle the equal comparison operator is a single = (not ==).
Within a trigger you have the special qualifiers :new and :old to reference the column values you are working on.
In update triggers only the :new qualifier is usable.
CREATE OR REPLACE TRIGGER tr_july
BEFORE INSERT
ON TBL_EVENT
BEGIN
IF EXTRACT (month from :new.EVN_DATE) = 7 THEN
RAISE_APPLICATION_ERROR(-20110, 'NOT ALLOWED TO INSERT RECORDS DURING JULY');
END IF;
END;
I have a table (Meeting) with date type attribute (MeetDate) and another varchar2 type attribute (WorkWeek). I'm trying to do an After trigger to fill in the WorkWeek field based on the MeetDate value using the to_char function. Tried the following codes separately and they compile without errors but when I try to insert a row with Null for WorkWeek, it gives me a 'mutating trigger/function may not see it' error. What am I doing wrong here? thanks in advance to any help.
--Code 1
Create or Replace Trigger Update_WorkWeek
After Insert On Meeting
For Each Row
Begin
Update Meeting
Set WorkWeek = (Select to_char(:new.MeetDate, 'YYYY IW') From Dual)
Where MeetID = :new.MeetID;
End;
/
show Errors;
--Code 2
Create or Replace Trigger Update_WorkWeek
After Insert On Meeting
For Each Row
Begin
if :New.WorkWeek is Null then
Update Meeting
Set WorkWeek = (Select to_char(:new.MeetDate, 'YYYY IW') From Dual)
Where MeetID = :new.MeetID;
End if;
End;
/
show Errors;
You just want a trigger to change the value of a column before it gets inserted - and it's on the same row, so you don't need an UPDATE:
Create or Replace Trigger Update_WorkWeek
BEFORE Insert On Meeting
For Each Row
Begin
:new.WorkWeek := to_char(:new.MeetDate, 'YYYY IW');
End;
/
show Errors;
You might want the column kept up-to-date if the MeetDate is changed, i.e.:
Create or Replace Trigger Update_WorkWeek
BEFORE Insert
OR Update OF MeetDate
On Meeting
For Each Row
Begin
:new.WorkWeek := to_char(:new.MeetDate, 'YYYY IW');
End;
/
show Errors;