I'm trying to create a trigger that will update a column date by one month whenever a new row is added.
This is what I have, can someone tell me what I'm doing wrong?
CREATE OR REPLACE TRIGGER tg_nextupdate
BEFORE INSERT
ON Vehicle
FOR EACH ROW
BEGIN
IF :NEW.NextUpdate = SYSDATE
THEN
SET NextUpdate = ADD_MONTHS(SYSDATE,1);
END IF;
END;
There is no need of IF-END IF block, whenever a new row is inserted, it will have sysdate. So, just update the NextUpdate to ADD_MONTHS(SYSDATE,1) directly. The check on IF :NEW.NextUpdate = SYSDATE is not required.
CREATE OR REPLACE TRIGGER tg_nextupdate
BEFORE INSERT
ON Vehicle
FOR EACH ROW
BEGIN
:NEW.NextUpdate = ADD_MONTHS(SYSDATE,1);
END;
You can encounter a problem with your code when NextUpdate contains only date, without of hours, minutes and seconds.
Try this:
CREATE OR REPLACE TRIGGER tg_nextupdate
BEFORE INSERT
ON Vehicle
FOR EACH ROW
BEGIN
IF :NEW.NextUpdate = trunc(SYSDATE)
THEN
SET NextUpdate = ADD_MONTHS(SYSDATE,1);
END IF;
END;
Or give us more details about what you want and what you get with your code.
This is what I have, can someone tell me what I'm doing wrong?
Assuming NextUpdate having for default value SYSDATE, as it has already been say, you IF is maybe "not necessary"...
... but, as of myself, I think the real issue is SYSDATE not guaranteeing to return the same value upon each call. If you don't believe me, try that http://sqlfiddle.com/#!4/1f810/2
So, your column might very well be properly initialized by SYSDATE to, say "October, 26 2014 18:50:10+0000". But, in your trigger, SYSDATE might very well return "October, 26 2014 18:50:11+0000". This would be bad luck, I admit. And maybe this is acceptable in your application. But in a more general case, this might easily become a hard to track bug.
Depending your needs, I would suggest one of those three options instead:
1) Assuming SYSDATE is a "magical value" meaning "hey trigger! Compute the right value for NextUpdate":
CREATE OR REPLACE TRIGGER tg_nextupdate
BEFORE INSERT
ON Vehicle
FOR EACH ROW
BEGIN
IF :NEW.NextUpdate <= SYSDATE
THEN
:NEW.NextUpdate := SYSDATE + INTERVAL '1' MONTH;
END IF;
END;
So, any time in the past will trigger the calculation of a new NextUpdate. Including 1s in the past.
2) Override NextUpdate from the trigger -- always:
CREATE TABLE Vehicle (value NUMBER(10),
NextUpdate DATE)
-- ^^^^^^^^^^^^^^^
-- No need for default here
-- as we override values
/
CREATE OR REPLACE TRIGGER tg_nextupdate
BEFORE INSERT
ON Vehicle
FOR EACH ROW
BEGIN
:NEW.NextUpdate := SYSDATE + INTERVAL '1' MONTH;
END;
/
INSERT INTO Vehicle(value) VALUES (1)
/
INSERT INTO Vehicle VALUES (2, TO_DATE('30/10/2014','DD/MM/YYYY'))
/
INSERT INTO Vehicle VALUES (3, TO_DATE('30/12/2014','DD/MM/YYYY'))
/
INSERT INTO Vehicle VALUES (4, NULL)
/
3) Set NextUpdate defaults to SYSDATE + INTERVAL '1' MONTH, allow the user to change that when inserting. If you need it, a trigger might keep the LEAST value (+/- the 1 second error as explained in preamble):
CREATE TABLE Vehicle (value NUMBER(10),
NextUpdate DATE DEFAULT SYSDATE + INTERVAL '1' MONTH)
-- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-- set default to the "most probable" value
/
CREATE OR REPLACE TRIGGER tg_nextupdate
BEFORE INSERT
ON Vehicle
FOR EACH ROW
DECLARE
LimitNextUpdate DATE := SYSDATE + INTERVAL '1' MONTH;
BEGIN
:NEW.NextUpdate := LEAST(LimitNextUpdate,
NVL(:NEW.NextUpdate,LimitNextUpdate));
-- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-- required if the used set the value to NULL
END;
/
INSERT INTO Vehicle(value) VALUES (1)
/
INSERT INTO Vehicle VALUES (2, TO_DATE('30/10/2014','DD/MM/YYYY'))
/
INSERT INTO Vehicle VALUES (3, TO_DATE('30/12/2014','DD/MM/YYYY'))
/
INSERT INTO Vehicle VALUES (4, NULL)
/
You will need to add extra logic (either in the trigger or as a check constraint) in order to reject NextUpdate in the past.
Related
I am trying to add x number of days to a variable within a table by deriving another date from the same table.
For example, in my BILLING table, it has 2 dates - BillDate and DueDate.
And so, I am trying to add a trigger before the insertion, such that it will takes in the BillDate and add 30 days to derive the DueDate.
While doing so, I got a bunch of errors, as follows:
dbfiddle
CREATE TABLE BILLING
(
BillDate DATE NOT NULL,
DueDate DATE NULL
);
-- Got ORA-24344: success with compilation error
CREATE TRIGGER duedate_trigger BEFORE INSERT ON BILLING
FOR EACH ROW
begin
set DueDate = :new.DueDate: + 30
end;
-- Got ORA-04098: trigger 'FIDDLE_FBHUOBXMWRPYBBXPIKTW.DUEDATE_TRIGGER' is invalid and failed re-validation
INSERT INTO BILLING
VALUES ((Date '2020-07-23'), NULL);
For the insertion, I have tried removing the NULL, but still I am getting a bunch of errors.
Any ideas?
Also, in the event, if the insertion statement also does includes in the due date too, will this affects the trigger? Trying to cater for 2 scenarios, generate a due date if not give, else if given, check if it is within 30 days from BillDate and update it... (likely I may have overthink/ overestimated that this is doable?)
CREATE OR REPLACE TRIGGER duedate_trigger BEFORE INSERT ON BILLING
FOR EACH ROW
begin
:new.DueDate := :new.BillDate + 30;
end;
INSERT INTO BILLING (BillDate ) values (sysdate);
CREATE OR REPLACE TRIGGER duedate_trigger
BEFORE INSERT ON billing
FOR EACH ROW
DECLARE
v_dueDate_derive NUMBER;
BEGIN
v_dueDate_derive = 30;
:new.DueDate = :new.BillDate + v_dueDate_derive;
END;
Days can be easily added by +, so it should not be the problem.
I believe there may be something wrong with INSERT itself.
Could you try to put INSERT like this?
INSERT INTO BILLING
VALUES (TO_DATE('2020-07-23'), NULL);
I need to figure out a way to create a trigger that prevents an accident from being inserted or updated if that accident has a day/time past the current system date.
Table code:
create table Accident
(Report_nr varchar2(4),
Accident_date date,
Location varchar2(20),
primary key (Report_nr));
My attempt (incomplete obviously) with pseudocode
CREATE OR REPLACE TRIGGER trigger1
BEFORE INSERT OR UPDATE ON accident
FOR EACH ROW
DECLARE
cur_date DATE := SYSDATE;
e EXCEPTION;
BEGIN
IF :new.accident_date <= cur_date THEN
[allow insertion/update]
ELSE
[dont allow]
END IF;
END;
I might not be anywhere close, to be honest this is not my strong suit. Any help or guidance would be appreciated. I'm running this on an Oracle server.
You are making it a bit more complicated than it needs to be. The default is for the insert to happen, so you just need to identify when it should not, and throw an exception at that point that causes the inert to be rejected; something like:
CREATE OR REPLACE TRIGGER trigger1
BEFORE INSERT OR UPDATE ON accident
FOR EACH ROW
BEGIN
IF :new.accident_date > SYSDATE THEN
RAISE_APPLICATION_ERROR(-20001, 'Accident date cannot be in the future');
END IF;
END;
/
Today's date works:
insert into accident values ('0001', date '2017-11-15', 'X');
1 row inserted.
Tomorrow's does not:
insert into accident values ('0002', date '2017-11-16', 'Y');
Error starting at line : 18 in command -
insert into accident values ('0002', date '2017-11-16', 'Y')
Error report -
ORA-20001: Accident date cannot be in the future
ORA-06512: at "MY_SCHEMA.TRIGGER1", line 3
ORA-04088: error during execution of trigger 'MY_SCHEMA.TRIGGER1'
Read more about raising exceptions.
I'm having a problem with a trigger code (table mutation and more!) and I can't find
what is the problem.
Basically, I have a table SEMESTER(id_semester, semester_name, begin_date, end_date).
On the insertion of a row, I want the semester_name to be updated with a value according
to what's in begin_date. For example, if the begin_date is '2000-01-01', I want the value of
semester_name to be W00 (for winter 2000).
My first try was to write an 'after insert' trigger, which didn't work because of a table mutation error. Here it is:
CREATE TRIGGER Test
BEFORE INSERT ON Semester
FOR EACH ROW
DECLARE
sem CHAR(1);
year CHAR(2);
BEGIN
-- begin_date is either 1, 5 or 9.
IF (EXTRACT(MONTH FROM :new.begin_date) = '1') THEN
saison := 'W';
ELSIF (EXTRACT(MONTH FROM :new.begin_date) = '5') THEN
saison := 'S';
ELSE
saison := 'F';
END IF;
year := TO_CHAR(:new.date_debut, 'MM');
UPDATE Semester
SET semester_name = CONCAT(sem, year)
WHERE id_semester = :new.id_semester;
END;
/
After, I tried to make a 'before insert' trigger, thinking it would work better but it does not.
Anyone could point me in the right direction?
Thanks!
Assuming id_semester is the primary key, instead of an UPDATE statement, you would just want to assign the :new.semester_name
:new.semester_name := concat( sem, year );
The mutanting table error occurs only with "each row" kind of triggers, try to change your after insert trigger for a "statement" type
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;
I am new to triggers in Oracle. I created an EVENT table with this syntax:
CREATE TABLE Event
(event_id NUMBER (3) NOT NULL,
event_date DATE NOT NULL,
venue_id NUMBER (2) NOT NULL,
concert_id NUMBER (3) NOT NULL
);
I want to create a trigger to ensure that concerts cannot run during the month of August. I tried the following code to create the trigger. The trigger was created successfully but after inserting a date with the month of August, it was inserted. This is not suppose to be.
CREATE OR REPLACE TRIGGER check_date
BEFORE INSERT ON event
DECLARE
event_date date;
BEGIN
IF (to_char(event_date, 'mm') = 8) THEN
raise_application_error(-20000, 'Concerts cannot be run during August');
END IF;
END;
First, the trigger needs to be a row-level trigger not a statement-level trigger. You want the trigger to be fired for every row that is inserted not just once for every statement. Declaring the trigger a row-level trigger allows you to see the data for each row that is being inserted.
Second, you don't want to declare a local variable event_date. You want to look at :new.event_date which is the event_date for the row that is being inserted.
If I put those two together
CREATE OR REPLACE TRIGGER check_date
BEFORE INSERT ON event
FOR EACH ROW
BEGIN
IF (to_char(:new.event_date, 'mm') = 8) THEN
raise_application_error(-20000, 'Concerts cannot be run during August');
END IF;
END;
then you'll get the behavior you want
SQL> insert into event values( 1, date '2012-08-01', 1, 1 );
insert into event values( 1, date '2012-08-01', 1, 1 )
*
ERROR at line 1:
ORA-20000: Concerts cannot be run during August
ORA-06512: at "SCOTT.CHECK_DATE", line 3
ORA-04088: error during execution of trigger 'SCOTT.CHECK_DATE'
As a general matter of cleanliness, you also want to compare strings with strings and numbers with numbers. So you would want either
to_number( to_char(:new.event_date, 'mm') ) = 8
or
to_char(:new.event_date, 'fmmm') = '8'
or
to_char(:new.event_date, 'mm') = '08'
change:
IF (to_char(event_date, 'mm') = 8) THEN
to:
IF (to_char(event_date, 'mm') = '08') THEN
You're comparing between string and number.