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;
Related
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'
I am trying to create a trigger to update the column reminder(datatype date) using the data of the column date_of_join(datatype date) of same table. Reminder should be 1st of January, next year.
My function is :
CREATE OR REPLACE FUNCTION update_reminder()
RETURNS TRIGGER AS
$$
BEGIN
NEW."reminder" := make_date(CAST (extract(year from timestamp NEW."date_of_join") AS INTEGER) ,2,3);
RETURN NEW;
END
$$ LANGUAGE
plpgsql;
My trigger is :
CREATE TRIGGER "Trigger1"
BEFORE INSERT ON faculty
for each row
EXECUTE PROCEDURE update_reminder();
It is giving me error : syntax error at or near "NEW"
How to do this?
date_trunc can be used to round to the beginning of the year; then you can add 1 year to arrive at January first of the following year:
NEW.reminder := date_trunc('year', NEW.date_of_join) + INTERVAL '1 year';
What I want to achive is check if hours count in day is higher than 24.
For example:
I've got entity hour with fields:
Integer id;
LocalDate date;
Integer hours;
Now I use post method to add new objects for example:
I add first one:
id - 1
date - 22.08.2018
hours - 10
I add second one:
id - 2
date - 22.08.2018
hours - 10
I add thrid one (hours count is now higher than 24 so I need an exception to be thrown )
id - 3
date - 22.08.2018
hours - 10
What I already have:
<sql>
ALTER TABLE hours_worked ADD CONSTRAINT more_than24h CHECK (hours >=0 AND hours <= 24)
</sql>
But this one only checks if I'm not adding more than 24 at once.
you can use constraint to check OTHER rows - you need trigger here, example for insert:
t=# create table i (id int, h int);
CREATE TABLE
t=# insert into i values(1,10),(1,10),(2,23);
INSERT 0 3
t=# create or replace function fi() returns trigger as $$ begin
if (select sum(h)+NEW.h > 24 from i where id = NEW.id) then
raise exception '%','over 24 for '||NEW.id;
end if;
return NEW; end;
$$ language plpgsql
;
CREATE FUNCTION
t=# create trigger ti before insert ON i for each row EXECUTE PROCEDURE fi();
CREATE TRIGGER
t=# insert into i values(1,1);
INSERT 0 1
t=# insert into i values(1,1);
INSERT 0 1
t=# insert into i values(1,1);
INSERT 0 1
t=# insert into i values(1,1);
INSERT 0 1
t=# insert into i values(1,1);
ERROR: over 24 for 1
CONTEXT: PL/pgSQL function fi() line 3 at RAISE
of course you need similar for UPDATE
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 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;
/