To get the year of a date
data type:
select extract(year from '2014-01-01'::date) as the_year;
the_year
----------
2014
If you only need the year then use a smallint with a check constraint
create table t (
the_year smallint check(
the_year between 0 and extract(year from current_date)
)
);
insert into t (the_year) values (2030);
ERROR: new row for relation "t" violates check constraint "t_the_year_check"
DETAIL: Failing row contains (2030).
insert into t (the_year) values (2014);
INSERT 0 1
But if you will store the whole date then it makes no sense to separate into 3 columns.
Note that the semantics of the column are up to the application. If a column is of the date
type but the application only considers the year then that column means the year.
Check the Date/Time Functions and Operators
One solution to the partial date problem pointed by @a_horse in the comments is to create a column indicating the precision of that date
create table t (
the_date date,
the_date_precision varchar(5)
);
insert into t (the_date, the_date_precision) values
(current_date, 'year'),
(current_date, 'month'),
(current_date, 'day')
;
select
case the_date_precision
when 'year' then to_char(the_date, 'YYYY')
when 'month' then to_char(the_date, 'YYYY-MM')
else to_char(the_date, 'YYYY-MM-DD')
end as the_date
from t
;
the_date
------------
2014
2014-02
2014-02-06
The above is the KISS aproach but I think the next implementation is more elegant
create table t (
the_date date,
the_date_precision smallint
);
insert into t (the_date, the_date_precision) values
(current_date, 1),
(current_date, 2),
(current_date, 3)
;
select
array_to_string(
(
string_to_array(to_char(the_date, 'YYYY-MM-DD'), '-')
)[1:the_date_precision]
, '-'
) as the_date
from t
;
the_date
------------
2014
2014-02
2014-02-06
That select expression could be turned into a function to be easier to reproduce. Or just a view
create view view_t as
select *,
array_to_string(
(
string_to_array(to_char(the_date, 'YYYY-MM-DD'), '-')
)[1:the_date_precision]
, '-'
) as the_date_output
from t
;
select * from view_t;
the_date | the_date_precision | the_date_output
------------+--------------------+-----------------
2014-02-06 | 1 | 2014
2014-02-06 | 2 | 2014-02
2014-02-06 | 3 | 2014-02-06
yyyy
simply isn't a date, it's just a year and as such it can not be stored in adate
column. If you really need this, you need to store this e.g. in avarchar
column. Another alternative would be to store year, month, day in three different (nullable) integer columns – a_horse_with_no_namedate
data type as you will be able to do all sorts of date arithmetic in a much easier way than with separate fields. There is also the benefit of the automaticconstraint
– Clodoaldo Netodate
column. I agree that if you always store a full date, adate
column is the only way to go. Unfortunately there is no nice solution for "partial dates" – a_horse_with_no_name