INSERT TRIGGER IN ORACLE - sql

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.

Related

How can i check that a user is 12 and over sql

How do I check that when a user enters their date of birth into my database that they are 12 and over?
I am using SQL plus.
Right now I have somthing like this:
create table test(dateofbirth date not null,
CONSTRAINT dateofbirth_ck CHECK(TO_CHAR(dateofbirth, 'YYYY-MM-DD') <= '2002-02-09')
);
The problem with this is that it does not update every day, so the '2002-02-09' should be changed every day.
I know what to do but I'm not sure how to execute it. I want to get the 'Dateofbirth' & sysdate - 12 years and if user is above 12 it will let them.
Oracle won't you let use sysdate in a check constraint (or other functions whose return value is not constant over time).
You can, however, write a trigger that implements the same logic:
create or replace trigger trg_mytable_dateofbirth
before insert or update on mytable
for each row
begin
if :new.dateofbirth > add_months(sysdate, -12 * 12)
then
raise_application_error( -20001,
'invalid date of birth - current value:'
|| to_char(:new.dateofbirth, 'yyyy-mm-dd')
|| ', limit as of today:'
|| to_char(add_months(sysdate, -12 * 12), 'yyyy-mm-dd')
);
end if;
end;
/
Demo on DB Fiddle:
insert into mytable(dateofbirth) values (date'2000-01-01')
-- 1 rows affected
insert into mytable(dateofbirth) values (date'2010-01-01')
-- ORA-20001: invalid date of birth - current value:2010-01-01, limit as of today:2008-02-10
-- ORA-06512: at "FIDDLE_MYQIVTFXTMKGROKMNOGB.TRG_MYTABLE_DATEOFBIRTH", line 4
-- ORA-04088: error during execution of trigger 'FIDDLE_MYQIVTFXTMKGROKMNOGB.TRG_MYTABLE_DATEOFBIRTH'

PL/SQL - Creating a trigger that prevents the update/insertion of a value with a date after the current date

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.

automatic update of date when inserting new row

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.

Trigger compilation error reasons

Hi guys I have two triggers I am meant to be be creating but I am getting compilation errors on both
this first is supposed to record evaluations of 0 to an audit table and the second is supposed to prevent the deletion of entries in which the date is less than todays date.
SQL> CREATE TABLE EVALUATION_AUDIT
2 (C_NAME VARCHAR (15), CO_ID NUMBER(7), E_DATE DATE,
3 V_ID NUMBER (7), C_EVALUATION NUMBER(1));
Table created.
SQL> CREATE OR REPLACE TRIGGER ZERO_EVAL
2 BEFORE INSERT OR UPDATE OF C_EVALUATION ON CUSTOMER_EVENT
3 FOR EACH ROW
4 WHEN (NEW.C_EVALUATION = 0)
5 BEGIN
6 SELECT C_NAME, CO_ID, E_DATE, V_ID, C_EVALUATION
7 FROM CUSTOMER_EVENT CE, CUSTOMER C, EVENT E
8 WHERE CE.C_ID = C.C_ID
9 AND CE.EVENT_ID = E.EVENT_ID
10 AND C_EVALUATION = NEW.C_EVALUATION;
11 INSERT INTO EVALUATION AUDIT
12 VALUES (:NEW.C_NAME, :NEW.CO_ID, :NEW.E_DATE, :NEW.V_ID, :NEW.C_EVALUATION);
13 END;
14 /
Warning: Trigger created with compilation errors.
SQL> CREATE OR REPLACE TRIGGER PASTEVENTS
2 BEFORE DELETE
3 ON EVENT
4 FOR EACH ROW
5 BEGIN
6 IF :OLD.E_DATE =< SYSDATE
7 THEN RAISE_APPLICATION_ERROR (-20002, 'CAN NOT DELETE PAST EVENT RECORDS');
8
9 END IF;
10 END;
11 /
Warning: Trigger created with compilation errors.
As Justin said, when you get created with compilation errors for any stored PL/SQL, type show errors, or you can query the user_errors table to see all outstanding errors on your objects.
From a quick scan, the first trigger is missing a colon when you reference NEW.C_EVALUATION in the select:
AND C_EVALUATION = :NEW.C_EVALUATION;
You need to select into something, though I'm not sure if it's necessary here as you have the values from the :NEW psuedorecord; not sure why you're selecting at all?
And the second has an incorrect operator, =< instead of <=:
IF :OLD.E_DATE <= SYSDATE
It's generally a good idea to prefix column names with the table alias to avoid ambiguity, e.g. SELECT C.C_NAME, ... if that column comes from the CUSTOMER table, etc. You could have another error in there is you have the same column on multiple tables. And it's good practise to list the column names in your INSERT too, i.e. INSERT INTO EVALUATION_AUDIT (C_NAME, ...) VALUES (...). With the missing underscore that #Dba spotted!
In your first trigger code you don't need to SELECT from the tables as you are just inserting the values from the table CUSTOMET_EVENT to EVALUATION_AUDIT. Also you have missed and underscore _ in table_name in EVALUATION_AUDIT in line 11.
CREATE OR REPLACE TRIGGER zero_eval
BEFORE INSERT OR UPDATE OF c_evaluation ON customer_event
FOR EACH ROW
WHEN (NEW.c_evaluation = 0)
BEGIN
INSERT INTO evaluation_audit(c_name, co_id, e_date, v_id, c_evaluation)
VALUES (:NEW.c_name, :NEW.co_id, :NEW.e_date, :NEW.v_id, :NEW.c_evaluation);
END;
/
In Your second code, it should be <= instead of =<
CREATE OR REPLACE TRIGGER pastevents
BEFORE DELETE
ON event
FOR EACH ROW
BEGIN
IF :OLD.e_date <= SYSDATE THEN
raise_application_error (-20002, 'CAN NOT DELETE PAST EVENT RECORDS');
END IF;
END;
/

Automatically update a column in a table with a trigger

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