Make a constraint for SYSDATE minus a date of birth - sql

I would love to know what I'm doing wrong ,
I would like to add a CHECK constraint in oracle to make sure a user is over 18
So I did
ALTER TABLE User
ADD CONSTRAINT check_age CHECK(TO_CHAR(SYSDATE, 'yyyy/mm/dd')- TO_CHAR(dateOfBirth, 'yyyy/mm/dd')> 18)
But Im receiving error
Cause: An attempt was made to use a date constant or system variable,
such as USER, in a check constraint that was not completely
specified in a CREATE TABLE or ALTER TABLE statement. For
example, a date was specified without the century.
*Action: Completely specify the date constant or system variable.
Setting the event 10149 allows constraints like "a1 > '10-MAY-96'",
which a bug permitted to be created before version 8.
Why it's wrong?
I still don't understand why I can't add it and I would kindly love if someone can explain it to me
Thanks

Starting in Oracle 11g, while you can't directly use sysdate in a constraint, you could get the same effect with a virtual column:
create function over_18 (
p_birth_date in User.dateOfBirth%type
) return varchar2 deterministic is
begin
return case when months_between(sysdate, p_birth_date) /12 > 18 then 'Y' else 'N' end;
end over_18;
/
alter table User add (over_18_ind as (cast(over_18(dateOfBirth) as varchar2(1))));
/
alter table User add constraint check_age check (over_18_ind = 'Y');
/
Based on an article here.
Even if Oracle allowed you to use sysdate in a constraint, your constraint wouldn't work anyway, since the formats you converted your dates to can't be implicitly cast back to numbers to subtract (I supplied an alternate age check).

Related

How to use the age() function in postgresql inside a CREATE TABLE block

I am trying to make a table called Citizens in postgresql using PGadmin and inside this table there is a column called ageand I want this age to be calculated and put inside this column when I insert a new row.
My code is as shown below:
create table citizens(
first_name VARCHAR(40),
last_name VARCHAR(45),
birth_date DATETIME DEFAULT NOW(),
ssid BIGINT PRIMARY KEY,
age INTERVAL AGE(TIMESTAMP birth_date),
);
I get this error message when I run this query:
ERROR: syntax error at or near "AGE"
LINE 6: age INTERVAL AGE(TIMESTAMP birth_date),
^
SQL state: 42601
Character: 156
What you are looking for is defining
age interval generated always as age(birth_date) virtual
Unfortunately Postgres does not support generated ... virtual. Your best option then is drop the column from table then create a view which derives that column. Something like:
create view citizens_vw as
select *, age(birth_date) as age
from citizens;
Or, even better, as the comment by #ChrisMaurer has it:
create view citizens_vw as
select *, extract (year from age(birth_date))::integer as age
from citizens;
A couple notes:
Postgres does not have a datatype DATETIME. You can use TIMESTAMP or just DATE.
NOW() seems like a poor choice for a default for birth_date. This
is a case where defining it as not null and not having a default is
a better option. Do not assume new citizens are just seconds or
days old. Sometimes it is better too handle the exception rather
that assuming incorrect data (which will likely never be updated).
Well at least IMHO.

PostgreSQL UPDATE statement treats keyword as a column

sorry for the noob question, just getting started with SQL and I have a problem that I could not resolve.
Basically, I created a table with "date of birth" column and chose wrong TYPE (integer), so every time I am trying to enter date of birth as "1998-01-04" it is just showing 1998. So, I decided to update the column to TEXT type.
Tried these queries
UPDATE users
SET date_of_birth = VARCHAR
It shows me error that there are no VARCHAR columns. Tried with as 'VARCHAR', still not working, tried as 'TEXT' and TEXT, still the same error.
What am I doing wrong?
Strings are as bad or worse than integers for this purpose. You want to use:
alter table alter column date_of_birth type date;
Note: If you have existing data in the column, you need to either use the using clause to convert it to a date. Or NULL it all out.
use this for your alter table statement
ALTER TABLE users
ALTER COLUMN date_of_birth TYPE VARCHAR(50);
I am not sure why you are storing date as varchar whereas you can store it as date, if you want to change that to date then use
ALTER TABLE users
ALTER COLUMN date_of_birth TYPE DATE;

DB2/400 - Auto generated timestamp on change (error)

I'm trying to create a table with a timestamp column that autogenerates with 'current timestamp' on each update of the record. I'm on DB2/400 (version V5R3) using ODBC driver.
That's the query:
CREATE TABLE random_table_name (
ID integer not null generated always as identity,
USERS_ID varchar (30),
DETAILS varchar (1000),
TMSTML_CREATE timestamp default current timestamp ,
TMSTMP_UPDATE timestamp not null generated always for each row on update as row change timestamp,
PRIMARY KEY ( ID )
)
I get this error (translated):
ERROR [42000] [IBM][iSeries Access ODBC Driver][DB2 UDB]SQL0104 - Token EACH not valid. Valid tokens: BIT SBCS MIXED.
Without the 'TMSTMP_UPDATE' row the query works. How can i solve this?
EDIT: Ok, i understand that in my DB2 version, the only way is to use triggers, but today AS400 seems to be evil with me.
I'm trying with this:
CREATE TRIGGER random_trigger_name
AFTER UPDATE ON random_table_name
REFERENCING NEW AS NEW_ROW
FOR EACH ROW MODE DB2SQL
BEGIN ATOMIC
SET NEW_ROW.TMSTM_UPDATE = CURRENT TIMESTAMP;
END
Error (translated):
ERROR [42000] [IBM][iSeries Access ODBC Driver][DB2 UDB]SQL0312 - Variable TMSTM_UPDATE not defined or not available.
The column TMSTM_UPDATE exist and it's a normal timestamp.
EDIT 2: I've solved the trigger problem by replacing 'after' with 'before'. Now everything works as expected. Thank you all!
There is a standard way to do it in iSeries DB2. It is documented here: IBM Knowledge center - Creating a row change timestamp column
You should change your table definition to:
TMSTMP_UPDATE TIMESTAMP NOT NULL FOR EACH ROW ON UPDATE AS ROW CHANGE TIMESTAMP
I am using it in tables in production over V7R2 and it works like a charm :) Hope it will be available for V5R3
EDIT
As Charles mentioned below unfortunately this feature is available since DB2 for i V6R1

check constraint for day and month only in Oracle SQL [duplicate]

This question already has answers here:
Using date in a check constraint, Oracle
(5 answers)
Closed 7 years ago.
So I need a constraint that will prevent a user from entering a day/month other than the first day of the quarter and last day of the quarter, as well as a date that the table will be 'locked' for editing. I did some searching online, and thought I found the answer, but when I tried it I got an error.
alter table TABLE_NAME
add constraint data_beg_dt_chk check
(trunc(data_beg_dt, 'DD-MON') = (trunc(to_date('01-JAN', 'DD-MON'))||(trunc(sysdate, 'YYYY')))
OR trunc(data_beg_dt, 'DD-MON') = (trunc(to_date('01-APR', 'DD-MON'))||(trunc(sysdate, 'YYYY')))
OR trunc(data_beg_dt, 'DD-MON') = (trunc(to_date('01-JUL', 'DD-MON'))||(trunc(sysdate, 'YYYY')))
OR trunc(data_beg_dt, 'DD-MON') = (trunc(to_date('01-OCT', 'DD-MON'))||(trunc(sysdate, 'YYYY'))));
I get the error:
SQL Error: ORA-02436: date or system variable wrongly specified in CHECK constraint
02436. 00000 - "date or system variable wrongly specified in CHECK constraint"
*Cause: An attempt was made to use a date constant or system variable,
such as USER, in a check constraint that was not completely
specified in a CREATE TABLE or ALTER TABLE statement. For
example, a date was specified without the century.
*Action: Completely specify the date constant or system variable.
Setting the event 10149 allows constraints like "a1 > '10-MAY-96'",
which a bug permitted to be created before version 8.
Does anyone know another way I can use just the day and month, but at the same time keep the current year that the user has enetered?
Thanks
You cannot have a date or datetime without a year, but you're trying to create such dates with the to_date() function. Dates are their own data type. You cannot create partial dates and concatenate them as if they were strings (though you could do that with actual char or varchar data). Moreover, you're making it a lot harder than it needs to be. Oracle's EXTRACT() function can extract day and / or month numbers from dates, and that's what you really want, especially given that you can do math on month numbers.
For example:
alter table TABLE_NAME
add constraint data_beg_dt_chk
check EXTRACT(day from data_beg_dt) = 1
and MOD(EXTRACT(month from data_beg_dt), 3) = 1;
Note that unlike the constraint you tried to write, the above does not constrain the dates to be in the current year. If Oracle even accepted such a constraint, it would result in a table in which every new year, every row would go from obeying the constraint to violating it, without any data having changed.
If you want the database to prevent the table from being modified outside of a certain date range, or if you want to restrict changes to certain rows based on the current date, then you should look into triggers instead of table constraints for that purpose.

Script SQL constraint for a number to fall within a Range?

How to script a constraint on a field in a table, for an acceptable range of values is between 0 and 100?
ALTER TABLE Table
ADD CONSTRAINT CK_Table_Column_Range CHECK (
Column >= 0 AND Column <= 100 --Inclusive
)
Try:
ALTER TABLE myTableName
ADD CONSTRAINT myTableName_myColumnName_valZeroToOneHundred
CHECK (myColumnName BETWEEN 0 AND 100)
This check would be inclusive - here is some info about BETWEEN from MSDN:
BETWEEN (Transact SQL)
A check constraint like "fieldname BETWEEN 0 AND 100" should do it.
According to me, the right question isn't "how" but "why".
This 0-100 rule sounds to me like a business rule. Why should it be implemented on the server/database side? If an incorrect value is entered, who will get the error message?
If the user gets the error message, wouldn't it be easier to have the code giving him the message before the transaction reaches the server?
What about range modification? May the rule change? I guess yes: rules ALLWAYS change. Can the range be updated from 0-100 to 101-200? In this case, what about values already entered in the database?