I am trying to create a trigger to calculate a derived attribute on each insert command. However I am getting compilation errors, I dont know where is the problem.
CREATE OR REPLACE TRIGGER NewTrigger
BEFORE INSERT
ON Dates FOR EACH ROW
BEGIN
SET :NEW.difference := :NEW.date1 - :NEW.date2;
END;
Show errors shows me this information:
LINE/COL ERROR
-------- -----------------------------------------------------------------
1/7 PL/SQL: SQL Statement ignored
1/11 PL/SQL: ORA-00922: missing or invalid option
It's not the trigger, it's the data type. If you substract a date from another date, the result is an interval, not another date:
CREATE TABLE dates (date1 DATE, date2 DATE, datediff DATE, numdiff NUMBER);
INSERT INTO dates (date1, date2) VALUES (sysdate, sysdate-1);
UPDATE dates SET numdiff = date1 - date2;
1 rows updated
UPDATE dates SET datediff = date1 - date2;
SQL Error: ORA-00932: inconsistent datatypes: expected DATE got DATE JULIAN
So, if the trigger stores the interval in a number, it compiles:
CREATE OR REPLACE TRIGGER newtriggernum
BEFORE INSERT ON dates FOR EACH ROW
BEGIN
:new.numdiff := :new.date1 - :new.date2;
END;
/
TRIGGER NEWTRIGGERNUM compiled
and if it stores the interval in a date, it doesn't:
CREATE OR REPLACE TRIGGER newtriggerdate
BEFORE INSERT ON dates FOR EACH ROW
BEGIN
:new.datediff := :new.date1 - :new.date2;
END;
/
Error(2,11): PL/SQL: ORA-00922: missing or invalid option
CREATE OR REPLACE TRIGGER NewTrigger
BEFORE INSERT ON Dates FOR EACH ROW
BEGIN
:NEW.difference := :NEW.date1 - :NEW.date2;
End;
/
Related
I have used this trigger:
CREATE OR REPLACE TRIGGER trg_chk_future
BEFORE INSERT ON your_table
FOR EACH ROW
BEGIN
IF( :new.date_time < sysdate )
THEN
RAISE_APPLICATION_ERROR( -20001, 'date_time must be in the future' );
END IF;
END;
However the current date comes up as an error when entered such as 12/12/2021 (today date) but 13/12/21 and any date after the today date works.
Any ideas what's wrong.
If you really inserted values you wrote in the question, who-knows-what you really inserted. Because, those are strings, not date values. Therefore, Oracle tried to convert them to valid DATE datatype values and - according to what you said - failed.
It means that you should actually insert DATE values, like the following example shows (trigger is exactly the same as you made it):
SQL> create table your_table (date_time date);
Table created.
SQL> CREATE OR REPLACE TRIGGER trg_chk_future
2 BEFORE INSERT ON your_table
3 FOR EACH ROW
4 BEGIN
5 IF( :new.date_time < sysdate )
6 THEN
7 RAISE_APPLICATION_ERROR( -20001, 'date_time must be in the future' );
8 END IF;
9 END;
10 /
Trigger created.
Testing: truncated sysdate is set to midnight (which was in the past, and thus rejected):
SQL> insert into your_table values (trunc(sysdate));
insert into your_table values (trunc(sysdate))
*
ERROR at line 1:
ORA-20001: date_time must be in the future
ORA-06512: at "SCOTT.TRG_CHK_FUTURE", line 4
ORA-04088: error during execution of trigger 'SCOTT.TRG_CHK_FUTURE'
May this year, also in the past:
SQL> insert into your_table values (date '2021-05-25');
insert into your_table values (date '2021-05-25')
*
ERROR at line 1:
ORA-20001: date_time must be in the future
ORA-06512: at "SCOTT.TRG_CHK_FUTURE", line 4
ORA-04088: error during execution of trigger 'SCOTT.TRG_CHK_FUTURE'
This is in the future, so it is accepted:
SQL> insert into your_table values (date '2021-12-26');
1 row created.
SQL>
As of
the current date comes up as an error when entered such as 12/12/2021 (today date)
Maybe you meant to say
IF( :new.date_time < trunc(sysdate))
which truncates sysdate to midnight today. In that case, today's date is also accepted:
SQL> CREATE OR REPLACE TRIGGER trg_chk_future
2 BEFORE INSERT ON your_table
3 FOR EACH ROW
4 BEGIN
5 IF( :new.date_time < trunc(sysdate))
6 THEN
7 RAISE_APPLICATION_ERROR( -20001, 'date_time must be in the future' );
8 END IF;
9 END;
10 /
Trigger created.
SQL> insert into your_table values (date '2021-12-12');
1 row created.
SQL>
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 want to put a constraint on a date of birth field for one of my db tables. Essentially I want to ensure pat_dob_dt is at least 16 years ago (from current date). I am using PostgreSQL 8.4.20 and used here for guidance:
CREATE OR REPLACE FUNCTION patient_dob_in_past()
RETURNS TRIGGER AS $$
BEGIN
-- check pat_dob_dt is in past --
IF ( NEW.pat_dob_dt > current_date - interval '16 years' ) THEN
RAISE EXCEPTION '% must be 16 years in past', NEW.pat_dob_dt
END IF;
RETURN NEW;
END;
$$ language 'plpgsql';
CREATE OR REPLACE TRIGGER patient_dob_in_past BEFORE UPDATE OR INSERT
ON patients FOR EACH ROW EXECUTE PROCEDURE patient_dob_in_past();
Unfortunately I am met with the following error
ERROR: syntax error at or near "END" at character 14
QUERY: SELECT $1 END IF
CONTEXT: SQL statement in PL/PgSQL function "patient_dob_in_past" near line 4
LINE 1: SELECT $1 END IF
Not sure where I am going wrong since I am following the psql docs for 8.4
EDIT
Semicolon fixeds function issue. I also get an error for my trigger
ERROR: syntax error at or near "TRIGGER" at character 19
LINE 1: CREATE OR REPLACE TRIGGER patient_dob_in_past BEFORE UPDATE ...
try:
CREATE OR REPLACE FUNCTION patient_dob_in_past()
RETURNS TRIGGER AS $$
BEGIN
-- check pat_dob_dt is in past --
IF ( NEW.pat_dob_dt > current_date - interval '16 years' ) THEN
RAISE EXCEPTION '% must be 16 years in past', NEW.pat_dob_dt;
END IF;
RETURN NEW;
END;
$$ language 'plpgsql';
also https://www.postgresql.org/docs/current/static/sql-createtrigger.html
CREATE OR REPLACE TRIGGER
will fail as it does not work with OR REPLACE - use just CREATE TRIGGER instead
also why not CHECK constraints? eg:
t=# create table q2(t timestamptz check (t < now() - '16 years'::interval));
CREATE TABLE
t=# insert into q2 select now();
ERROR: new row for relation "q2" violates check constraint "q2_t_check"
DETAIL: Failing row contains (2017-10-10 11:41:01.062535+00).
t=# insert into q2 select now() - '16 years'::interval;
ERROR: new row for relation "q2" violates check constraint "q2_t_check"
DETAIL: Failing row contains (2001-10-10 11:41:13.031769+00).
t=# insert into q2 select now() - '16 years'::interval -'1 second'::interval;
INSERT 0 1
update
In case of existing previous values that do not match check constraint - you can delay check with NOT VALID, eg:
t=# create table q2(t timestamptz);
CREATE TABLE
t=# insert into q2 select now();
INSERT 0 1
t=# alter table q2 add constraint q2c check (t < (now() - '16 years'::interval)) not valid;
ALTER TABLE
t=# insert into q2 select now();
ERROR: new row for relation "q2" violates check constraint "q2c"
DETAIL: Failing row contains (2017-10-10 11:56:02.705578+00).
You missed semicolon at the end of the line.
RAISE EXCEPTION '% must be 16 years in past', NEW.pat_dob_dt;
Table structure is
Name Null Type
------------- ---- ------------
T_NO NUMBER
T_NAME VARCHAR2(10)
ENTERING_TIME TIMESTAMP(6)
LEAVING_TIME TIMESTAMP(6)
TO_DATE DATE
Trigger
create or replace trigger t4
before insert
on t4
for each row
declare
d_entering_time timestamp(6):=to_char('09:00:00AM','HH12:MM:SSAM');
begin
if (:new.entering_time <= d_entering_time) then
raise_application_error
(-20002,'Date of joining cannot be after system date.');
end if;
end;
and my inserting query
insert INTO t3 (entering_time) values ( TO_date('8:31:51AM','HH:MI:SSAM'))
I am getting the following error:
SQL Error: ORA-06502: PL/SQL: numeric or value error: character to number conversion error
ORA-06512: at "SYSTEM.T3", line 2
ORA-04088: error during execution of trigger 'SYSTEM.T3'
06502. 00000 - "PL/SQL: numeric or value error%s"
*Cause:
*Action:
Can any one suggest me where error occuring?
Try this:
CREATE OR REPLACE TRIGGER T4
BEFORE INSERT
ON T3
FOR EACH ROW
DECLARE
SSSSS_ENTERING_TIME NUMBER := 32400;
BEGIN
IF ( TO_NUMBER ( TO_CHAR ( :NEW.ENTERING_TIME,
'SSSSS' ) ) <= SSSSS_ENTERING_TIME )
THEN
RAISE_APPLICATION_ERROR (
-20002,
'Date of joining cannot be after system date.' );
END IF;
END;
INSERT INTO
T3 ( ENTERING_TIME )
VALUES
( TO_TIMESTAMP ( '01/01/2010 8:31:51AM',
'DD/MM/RR HH:MI:SSAM.FF' ) );
Note: I have extracted the total seconds from the time part and converted to a number for comparing. Hence I used 32400 seconds which is nothing but the actual 9 AM.
In Oracle we can turn dates into numbers and apply arithmetic to them in a variety of ways.
Hence to_char(some_date, 'SSSSS') gives us its time element as the number of seconds since midnight.
It looks like there is some errors in your code,
You are trying to store a string literal into to timestamp variable.
d_entering_time timestamp(6):=to_char('09:00:00AM','HH12:MM:SSAM');
It's HH12:MI:SSAM, not HH12:MM:SSAM, MI is for minute and MM is for month.
You can try like this,
CREATE OR REPLACE TRIGGER t4
BEFORE INSERT ON t3 FOR EACH ROW
DECLARE
d_entering_time TIMESTAMP :=to_timestamp('09:00:00AM','HH12:MI:SSAM.FF');
BEGIN
IF (:NEW.entering_time <= d_entering_time) THEN
raise_application_error (-20002,'Date of joining cannot be after system date.');
END IF;
END;
Insert query,
INSERT INTO t3 (entering_time) VALUES ( to_timestamp('8:31:51AM','HH:MI:SSAM.FF'));
I wrote the below trigger to prevent users from allocating a Class to a Session if the Class Date does not match the day of the week the Session is on.
CREATE OR REPLACE TRIGGER trig_alternative_classDate
AFTER INSERT OR UPDATE ON ALTERNATIVE
FOR EACH ROW
DECLARE
classdate CHAR;
sessionday VARCHAR;
BEGIN
SELECT to_char(to_date(class.class_date), 'Day') INTO classdate, sessions.day INTO sessionday
FROM SESSIONS, CLASS, DUAL, LOCATION, ALTERNATIVE
WHERE class.class_id = alternative.class_id
AND alternative.location_id = location.location_id
AND sessions.location_id = location.location_id;
IF sessions.day != to_char(to_date(class.class_date), 'Day')
THEN raise_application_error(-20999,'Invalid Class Date - Class Date does not match Session Day');
END IF;
END;
/
However I get an error message when I run the trigger
Warning: Trigger created with compilation errors.
SQL> show error trigger trig_alternative_classDate
Errors for TRIGGER TRIG_ALTERNATIVE_CLASSDATE:
LINE/COL ERROR
-------- -----------------------------------------------------------------
5/2 PL/SQL: SQL Statement ignored
5/80 PL/SQL: ORA-00923: FROM keyword not found where expected
Could someone please help?
Remove second INTO - only one INTO is needed:
INTO classdate, sessions.day INTO sessionday