Why oracle sql not allow sysdate in table creation time for count age? - oracle

Query....
I want to try to make check constraints on birthdate and check age should be greater than 18.
Create table emp
(
Birthdate date,
Check( MONTHS_BETWEEN(SYSDATE,Birthdate))
);
Error on above query....why?
Anyone help me...
Why oracle sql not allow sysdate in table creation time for count age?

SYSDATE is not allowed because the constraint must be either "true" or "false" at any time you look at the data. If you were able to use SYSDATE in a check constraint, you could insert a row that satisfied the constraint at that time, but the constraint would be violated later. No good!
In your example, once the constraint is satisfied at insert time, it can't become "not satisfied" later. But here you are asking Oracle to think. It can't. It just doesn't allow you to use SYSDATE in constraints. Period.
Instead, you should write a simple trigger to do the check for you. Note that you are missing the comparison to 18 * 12 in your purported check constraint; MONTHS_BETWEEN may give some weird results in some cases; and it is always best to write code that mirrors your thinking: in this case the condition (in a trigger, not a check constraint) should be ***
sysdate >= birthdate + interval '18' year
*** EDIT: As Alex Poole points out below, adding INTERVAL to a date may sometimes be as weird as MONTHS_BETWEEN. The safe way to write the check is
sysdate >= add_months ( birthdate, 18 * 12 ) -- age >= 18 years or 18 * 12 months
(That is how I would write it - with the comment to explain the purpose, and 18 * 12.)

Maybe try:
SQL> create table person
(name varchar2(100),
dob date,
created_date date default sysdate not null,
constraint dob_check check
(
dob <= add_months(trunc(created_date), (12*18)*-1)
)
)
Table created.
SQL> insert into person(name,dob) values ('Bob', to_date('19740101','YYYYMMDD'))
1 row created.
SQL> commit
Commit complete.
SQL> insert into person(name,dob) values ('Jane', to_date('20050101','YYYYMMDD'))
insert into person(name,dob) values ('Jane', to_date('20050101','YYYYMMDD'))
Error at line 17
ORA-02290: check constraint (MYUSER.DOB_CHECK) violated

This is because of Oracle limitation. The reason is SYSDATE is non-deterministic. You can get a different result every time you call it. So the outcome (true/false) can (will) change over time. So Oracle can't guarantee that the expression is always true for every row.
See also https://asktom.oracle.com/pls/apex/asktom.search?tag=sysdate-in-check-constraints

Related

Oracle -- Datatype of column which can store value "13:45"

We need to store a value "13:45" in the column "Start_Time" of an Oracle table.
Value can be read as 45 minutes past 13:00 hours
Which datatype to be used while creating the table? Also, once queried, we would like to see only the value "13:45".
I would make it easier:
create table t_time_only (
time_col varchar2(5),
time_as_interval INTERVAL DAY TO SECOND invisible
generated always as (to_dsinterval('0 '||time_col||':0')),
constraint check_time
check ( VALIDATE_CONVERSION(time_col as date,'hh24:mi')=1 )
);
Check constraint allows you to validate input strings:
SQL> insert into t_time_only values('25:00');
insert into t_time_only values('25:00')
*
ERROR at line 1:
ORA-02290: check constraint (CHECK_TIME) violated
And invisible virtual generated column allows you to make simple arithmetic operations:
SQL> insert into t_time_only values('15:30');
1 row created.
SQL> select trunc(sysdate) + time_as_interval as res from t_time_only;
RES
-------------------
2020-09-21 15:30:00
Your best option is to store the data in a DATE type column. If you are going to be any comparisons against the times (querying, sorting, etc.), you will want to make sure that all of the times are using the same day. It doesn't matter which day as long as they are all the same.
CREATE TABLE test_time
(
time_col DATE
);
INSERT INTO test_time
VALUES (TO_DATE ('13:45', 'HH24:MI'));
INSERT INTO test_time
VALUES (TO_DATE ('8:45', 'HH24:MI'));
Test Query
SELECT time_col,
TO_CHAR (time_col, 'HH24:MI') AS just_time,
24 * (time_col - LAG (time_col) OVER (ORDER BY time_col)) AS difference_in_hours
FROM test_time
ORDER BY time_col;
Test Results
TIME_COL JUST_TIME DIFFERENCE_IN_HOURS
____________ ____________ ______________________
01-SEP-20 08:45
01-SEP-20 13:45 5
Table Definition using INTERVAL
create table tab
(tm INTERVAL DAY(1) to SECOND(0));
Input value as literal
insert into tab (tm) values (INTERVAL '13:25' HOUR TO MINUTE );
Input value dynamically
insert into tab (tm) values ( (NUMTODSINTERVAL(13, 'hour') + NUMTODSINTERVAL(26, 'minute')) );
Output
you may either EXTRACT the hour and minute
EXTRACT(HOUR FROM tm) int_hour,
EXTRACT(MINUTE FROM tm) int_minute
or use formatted output with a trick by adding some fixed DATE
to_char(DATE'2000-01-01'+tm,'hh24:mi') int_format
which gives
13:25
13:26
Please see this answer for other formating options HH24:MI
The used INTERVAL definition may store seconds as well - if this is not acceptable, add CHECK CONSTRAINT e.g. as follows (adjust as requiered)
tm INTERVAL DAY(1) to SECOND(0)
constraint "wrong interval" check (tm <= INTERVAL '23:59' HOUR TO MINUTE and EXTRACT(SECOND FROM tm) = 0 )
This rejects the following as invalid input
insert into tab (tm) values (INTERVAL '13:25:30' HOUR TO SECOND );
-- ORA-02290: check constraint (X.wrong interval) violated

Oracle constraint - compare two string like two times

I would like to create table witj constrain which compare two varchar2(8 char) columns as time.
Is it possible ?
I've made somethink like this, but it doesn't work :(
CONSTRAINT "my_constraint" CHECK (to_number(to_char(to_date(window_stop, 'hh24:mi:ss'), 'sssss')) > to_number(to_char(to_date(window_start, 'hh24:mi:ss'), 'sssss'))) ENABLE
Thx for all help.
Paul.
Your constraint will get "ORA-02436: date or system variable wrongly specified in CHECK constraint" because if you don't provide the date elements then to_date() defaults to the first day of the current month; therefore the result of evaluating the constraint check could change after the data is inserted, which isn't allowed. It can't actually change in this specific case, but a general rule is being applied, causing the error.
You could use a nominal fixed date instead:
CONSTRAINT "my_constraint"
CHECK (
to_number(to_char(to_date('2000-01-01 ' || window_stop, 'YYYY-MM-DD hh24:mi:ss'), 'sssss'))
> to_number(to_char(to_date('2000-01-01 ' || window_start, 'YYYY-MM-DD hh24:mi:ss'), 'sssss'))
) ENABLE
db<>fiddle
There isn't much point converting to a number though, just compare the dates:
CONSTRAINT "my_constraint"
CHECK (
to_date('2000-01-01 ' || window_stop, 'YYYY-MM-DD hh24:mi:ss')
> to_date('2000-01-01 ' || window_start, 'YYYY-MM-DD hh24:mi:ss')
) ENABLE
db<>fiddle
Or - if you are confident that values are always going to be valid times and will have leading zeros - just compare the strings:
CONSTRAINT "my_constraint" CHECK (window_stop > window_start) ENABLE
db<>fiddle
You could also store the times as nominal dates, or as number of seconds past midnight, or as an interval, which would make it easier to prevent completely invalid values being used (e.g. '99:00:00'). But you may have a real date you can use instead - depends if these are tied to real dates, or e.g. shift patterns, or similar - which would allow you to handle windows crossing midnight. (What you should not do is store a related date and time as separate fields, but no indication that is what you are doing here.)
As I said, it is always a very bad idea to use varchar2 for date fields. Let me show you how your constraint is so easy when the fields are dates
SQL> create table t ( c1 date , c2 date ) ;
Table created.
SQL> alter session set nls_date_format='dd.mm.yyyy hh24:mi:ss' ;
Session altered.
SQL> insert into t values ( to_date('11.08.2020 14:00:00') , to_date('11.08.2020 14:10:00') ) ;
1 row created.
SQL> commit ;
Commit complete.
SQL> select * from t ;
C1 C2
------------------- -------------------
11.08.2020 14:00:00 11.08.2020 14:10:00
SQL> select ( c2 - c1 ) * 24 * 60 * 60 from t ;
(C2-C1)*24*60*60
----------------
600
Now, let's add the constraint
SQL> alter table t add constraint chk_test check ( c2 > c1 ) ;
Table altered.
SQL> insert into t values ( to_date('11.08.2020 14:11:00') , to_date('11.08.2020 14:10:00') ) ;
insert into t values ( to_date('11.08.2020 14:11:00') , to_date('11.08.2020 14:10:00') )
*
ERROR at line 1:
ORA-02290: check constraint (SYS.CHK_TEST) violated
You want to store times, which are dates, and you want to compare that the window_stop is greater than the window_start. Instead of making such complicated conversions, just store the fields as dates. Despite the fact that is the properly data type for the data you want to store, the constraint will work much more better.

CURRENT_DATE Oracle

I'm having hard time to add a new colomn of date of birth with the check rule age between 18 and 65.
I'm using sqplus with Oracle
Alway getting the error message ORA00920
Need your help please
ALTER TABLE Vendeur ADD (dateNaissance DATE,
dateDebutProjet DATE NOT NULL,
DateFinProjet DATE NOT NULL,
CONSTRAINT chk_date_Birth CHECK ((TRUNC(CURRENT_DATE)-dateNaissance)
BETWEEN 18 AND 65),
CONSTRAINT chk_date_Projet CHECK (DateFinProjet > dateDebutProjet));
if it can help, the solution without triggers (since we didn't learn hem at that time):
ALTER TABLE Vendeur ADD (dateNaissance DATE,
debutProjet DATE DEFAULT '01/01/1000' NOT NULL,
finProjet DATE DEFAULT '02/01/1000' NOT NULL,
dateDuJour Date DEFAULT CURRENT_DATE,
CONSTRAINT chk_date_Projet CHECK (finProjet > debutProjet),
CONSTRAINT chk_date_Birth CHECK ((dateDuJour - dateNaissance)\365 BETWEEN 18 AND 65)
);
Check constraints cannot call non-deterministic functions like CURRENT_DATE. Check constraints are supposed to always be true, weird things might happen if check constraints aged out.
The below sample code shows one of the errors you might get trying to use CURRENT_DATE in a check constraint:
SQL> create table test1(a date);
Table created.
SQL> alter table test1 add constraint test1_ck1 check(a > date '2000-01-01');
Table altered.
SQL> alter table test1 add constraint test1_ck2 check(a > current_date);
alter table test1 add constraint test1_ck2 check(a > current_date)
*
ERROR at line 1:
ORA-02436: date or system variable wrongly specified in CHECK constraint
Create a trigger to workaround this problem:
create or replace trigger test1_date_gt_today
before update or insert of a on test1
for each row
begin
if :new.a is null or :new.a < current_date then
raise_application_error(-20000, 'The date cannot be earlier than today.');
end if;
end;
/
Below is an example of one INSERT working, and one failing to meet the condition in the trigger:
SQL> insert into test1 values(sysdate + 1);
1 row created.
SQL> insert into test1 values(sysdate - 1);
insert into test1 values(sysdate - 1)
*
ERROR at line 1:
ORA-20000: The date cannot be earlier than today.
ORA-06512: at "JHELLER.TEST1_DATE_GT_TODAY", line 3
ORA-04088: error during execution of trigger 'JHELLER.TEST1_DATE_GT_TODAY'

Checking if beneficiary is adult (more than 18 year) when inserting

In "beneficiary" table I have "birthDate" column, when beneficiary registered, i want check, if it is not adult (no more than 18 year), I want disallow register
For example today is "2017-04-05" and if beneficiary tries insert birth date "1999-04-06", I want disallow insert. if birth date is "1999-04-05" or earlier, insert must happen.
After searched found trigger are for this? I tried several ways but not realized how to add this restriction to my table.
You can use this:
(If you never update birthDate column, then you can remove this piece: OR UPDATE and recreate trigger )
CREATE TRIGGER trg
BEFORE INSERT OR UPDATE ON beneficiary
FOR EACH ROW
BEGIN
IF :NEW.birthDate > add_months(sysdate , -12*18) then
RAISE_APPLICATION_ERROR(-20001, 'Underage user');
END IF;
END;
In SQL Server databases, you can use the DATEDIFF function in your trigger:
DATEDIFF(YEAR, birthDate, SYSDATE) >= 18
In Oracle databases, you can use the MONTHS_BETWEEN function:
months_between(SYSDATE, birthDate)/12 >= 18

Creating trigger for date of Birth

Please can someone enlighten me on how to create trigger for date of birth having upper limit of 60 and lower limit of 20. I am working on an assignment that requires constraint on staff age range for a shipping company on oracle 11g. thank you
Don't use triggers to enforce relational integrity, use constraints. That's what they're for.
You haven't troubled yourself to provide us with the table structure, so you will need to tweak this for your actual table.
alter table employees
add constraint minimum_age_ck check
(hire_date >= add_months(date_of_birth, 240);
alter table employees
add constraint maximum_age_ck check
(hire_date <= add_months(date_of_birth, 720);
Of course, if your employees table lacks a hire_date column you got a big problem.
Thanks. I finally used this
alter table staff
add constraint minimum_age_ck check
((dateOfBirth >= add_months(dateOfBirth, -18)));
and this
alter table staff
add constraint maximum_age_ck check
((dateOfBirth <= add_months(date_of_birth, 60)));
Will try inserting the values and check if the constraints are valid.
Meanwhile a check on the above answer by inserting values proved it was not valid. i also tried using this
ALTER TABLE STAFF
ADD CONSTRAINT minimum_age_ck
CHECK ( (months_between (sysdate, dateOfBirth) >= 240));
CHECK ( (months_between (sysdate, dateOfBirth) >= 240))
*
ERROR at line 3:
ORA-02436: date or system variable wrongly specified in CHECK constraint
Is sysdate not a valid usable comparison for months_between?

Resources