How to extract months and year from a timestamp in postgreSQL table - sql

With PostgreSQL, while using the AGE() function to display age from date of birth in my table, with query
SELECT date_of_birth, AGE(NOW(), date_of_birth) AS age FROM person;
Got the following,result.
I want to select only year and month from the timestamp, so the result be like "1 year 3 mons",
Tried using EXTRACT(),it outs only one field at a time,
SELECT date_of_birth, EXTRACT(YEAR FROM AGE(NOW(), date_of_birth)) AS age FROM person;
and, TO_CHAR(),
SELECT date_of_birth, TO_CHAR(AGE(NOW(), date_of_birth), 'YY-MM') AS age FROM person;
But the result is not in the desired format, can anyone please explain how to use the above methods in correct way or any other methods to get the desired result.

You can format the interval using TO_CHAR():
TO_CHAR(AGE(NOW(), date_of_birth), 'YY "Year" MM "month"') AS age
However, a more colloquial solution is to build the parts separately:
CONCAT_WS(' ',
(CASE WHEN EXTRACT(YEAR FROM v.age) = 0 THEN NULL
WHEN EXTRACT(YEAR FROM v.age) = 1 THEN '1 year'
ELSE EXTRACT(YEAR FROM v.age) || ' years'
END),
(CASE WHEN EXTRACT(MONTH FROM v.age) = 1 THEN '1 month'
ELSE EXTRACT(MONTH FROM v.age) || ' months'
END)
) as age
This handles things like "1 year" versus "2 years".
Here is a db<>fiddle.

Related

PostgreSQL how to get closest birthday date

I have a table called birthdays with 2 columns name and date.
Name is string value, date is date (Looks like this: 1989-07-28 00:00:00)
How can i get closest birthday, according to day i am checking, for example NOW()
Using PostgreSQL
The question is determining if the year is this year or next year:
select (case when to_char(dob, 'MM-DD') >= to_char(now(), 'MM-DD')
then to_date(to_char(current_date, 'YYYY') || '-' || to_char(dob, 'MM-DD'), 'YYYY-MM-DD')
else to_date(to_char(current_date, 'YYYY') || '-' || to_char(dob, 'MM-DD'), 'YYYY-MM-DD') + interval '1 year'
end)
from (values ('1989-07-28'::date)) v(dob);
Assuming you are searching for forward-looking birthdays, the query below takes the minimum difference in days starting from current date
Select "Date" - Current_Date as diff,
"Date" as Dob,
Name
from birthdays
Where ("Date"- Current_Date) > 0
Order by 1 asc
limit 1;
First off in Postgres if your column is defined as DATE then it does not appear as "1989-07-28 00:00:00". Dates in Postgres do not have time components, so no "00:00:00" (unless you have done something with datestyle. But that is actually immaterial here.
Postgres dates can be directly subtracted to get the days number of between them, with the result either positive or negative depending upon which date occurs first. To
get the "closest" to a specific data just take the absolute value.
with birthdays (name, bday) as
( values ('George', date '2020-10-18')
, ('Gloryann', date '2020-11-02')
, ('Phyllis', date '2020-10-09')
, ('Sam', date '2020-06-18')
)
select name, bday birthday
from birthdays
order by abs(current_date-bday)
limit 1;
Caution: Do not use date as an object name. Date is both a Postres and SQL standard reserved word. While using it may be permitted currently Postges would be within their right to enforce the predefined meaning at anytime, potentially causing major issues for your app. Play it safe Do Not use reserved words as object names.
Thanks guys, it works perfect this way:
select name, date, (case when to_char(date, 'MM-DD') >= to_char(now(), 'MM-DD') then
to_date(to_char(current_date, 'YYYY') || '-' || to_char(date, 'MM-DD'), 'YYYY-MM-DD')
else to_date(to_char(current_date, 'YYYY') || '-' || to_char(date, 'MM-DD'), 'YYYY-MM-DD') + interval '1 year' end)
from birthdays order by 3

Oracle SQL to get first day of month

Requirement is input date should produce first day of the month.
Condtions are:
If the date entered is between 16-nov to 30-nov, then the first day will be 16-nov.
if the date entered is between 1-nov to 15-nov , then the first day will be 01-nov.
for all other month it should return 01st day of corresponding month.
Building on Tim Biegeleisen's solution, simplifying it and avoid the date-to-text-to-date conversions. Note the use of TRUNC to get the first date of the period.
SELECT
CASE
WHEN EXTRACT(MONTH FROM DATE_COL) = 11 AND EXTRACT(DAY FROM DATE_COL) >= 16
THEN TRUNC(DATE_COL, 'MONTH') + 15
ELSE TRUNC(DATE_COL, 'MONTH')
END AS FIRST_OF_MONTH
FROM T1
I used a lengthy CASE expression to handle this, containing the logic for the three cases you mentioned in your question.
SELECT CASE WHEN EXTRACT(month FROM date) = 11 AND
EXTRACT(day FROM date) >= 16
THEN TO_DATE(EXTRACT(year FROM date) || '-11-16', 'yyyy-mm-dd')
ELSE TO_DATE(EXTRACT(year FROM date) || '-' || EXTRACT(month FROM date) ||
'-01', 'yyyy-mm-dd')
END AS newDate
FROM yourTable

Having custom year, not calendar year

I would like the year to begin from August and end in the following July. Is there a simple way of doing this apart from writing the ugly IFELSE statements that I will have to write for each year? Ideally, I'd like to have year like 2009-10, 2010-11 etc. I'm using Postgres.
Here's an example query that I'd like to manipulate in order to get the custom year.
Select extract(year from created) as Year, type, count(*)
from table
group by Year, type
order by Year
Any help would be appreciated, TIA.
Consider setting up a Dates table. With this Dates table, you can associate a variety of data with each date, such as fiscal year, quarter, month #, whether it's a holiday, weekend, etc.
SELECT (CASE WHEN (EXTRACT(month FROM created) > 6) THEN CAST(EXTRACT(year FROM created) AS text) || '-' || CAST(EXTRACT(year FROM created) +1 AS text) ELSE (CAST(EXTRACT(year FROM created) - 1 AS text) || '-' || CAST(EXTRACT(year FROM created) AS text)) END) AS CustomYear
FROM asmt.testscores order by created desc LIMIT 10000
This seems to have done the trick.
select
extract(year from created + interval '6 months') as "Year",
type,
count(*)
from table
group by 1, 2
order by 1

postgresql case when date and sorting by date

Im trying to filter results by a specific date but when I use the below code I only get results where CloseDate matches current date. It doesnt include the results from ResolvedDate when CloseDate is Null.
I tried with :
(CASE WHEN (Closedate IS Null) then ResolvedDate, ELSE CloseDate END) AS FinalDate
but then it states:
"column FinalDate does not exist"
Any other way I can do this?
Here is the code thus far. Appreciate your help.
SELECT
id,
(CASE WHEN (Closedate IS Null) then ResolvedDate, ELSE CloseDate END),
FROM cases
WHERE (EXTRACT (month FROM Closedate) = EXTRACT(month FROM current_date))
AND ( EXTRACT(day from Closedate) = EXTRACT(day FROM current_date))
Assuming you want to match the year as well:
SELECT id, COALESCE(Closedate, ResolvedDate) AS cdate
FROM cases
WHERE date_trunc('day', COALESCE(Closedate, ResolvedDate))
= date_trunc('day', now())
Per documentation: COALESCE, date_trunc()
If you want to ignore the year:
WHERE to_char(COALESCE(Closedate, ResolvedDate), 'MMDD')
= to_char(now(), 'MMDD')
A bit more on that:
How do you do date math that ignores the year?
You should use COALESCE function http://www.postgresql.org/docs/current/static/functions-conditional.html
Try this:
SELECT
id,
COALESCE(CloseDate, ResolvedDate) AS FinalDate
FROM
cases
WHERE
(EXTRACT (month FROM COALESCE(CloseDate, ResolvedDate)) = EXTRACT(month FROM current_date)) AND
(EXTRACT (day from COALESCE(CloseDate, ResolvedDate)) = EXTRACT(day FROM current_date))
That should do it...

How to get only date/month part from a date in postgres SQL

Hello All
I have a table in my pg admin database.There is an employee table in this table.Having the field:-
1)name
2)date_of_birth
Now the scenario is that I want to know the birth day for current date and upcoming 20 days
For example if current date is 28-Jan-2013 then
1)from_date=28-Jan-2013
2)to_date=16-feb-2013
I want to select all the records from the table for which the
date_of_birth
lies between 28-Jan and 16-feb
Try this:
SELECT *
FROM bdaytable
WHERE bdate >= '2013-01-28'::DATE
AND bdate <= '2013-02-16'::DATE;
You may also try overlaps:
SELECT *
FROM bdaytable
WHERE (bdate, bdate)
OVERLAPS ('2013-01-28'::DATE, '2013-02-16'::DATE);
with extract, month, day:
SELECT *
FROM bdaytable
WHERE Extract(month from bdate) >= Extract(month from '2013-01-28'::DATE)
AND Extract(month from bdate) <= Extract(month from '2013-02-16'::DATE)
AND Extract(day from bdate) >= Extract(day from '2013-01-28'::DATE)
AND Extract(day from bdate) <= Extract(day from '2013-02-16'::DATE);
Incorporating Now() and interval to make the query dynamic with current date:
SELECT *
FROM bdaytable
WHERE Extract(month from bdate) >= Extract(month from Now())
AND Extract(month from bdate) <= Extract(month from Now() + Interval '20 day')
AND Extract(day from bdate) >= Extract(day from Now())
AND Extract(day from bdate) <= Extract(day from Now() + Interval '20 day');
select *
from employee
where
to_char(date_of_birth, 'MMDD') between
to_char(current_date, 'MMDD') and to_char(current_date + 20, 'MMDD')
I think this should work. Use interval.
SELECT *
FROM Employee
WHERE Date_Of_Birth >= now() AND Date_Of_Birth <= now() + interval '20 day'
If you want to get any birth date between these days (and year doesn't matter), then that would be slightly different.
EDIT
If year doesn't matter, while I'm sure there is a better way, this could work. Basically it's just converting all years to a common year (in this case 2000) -- you could do the same with your input parameter as well.
(Date_Of_Birth + (2000-EXTRACT(YEAR FROM Date_Of_Birth) || ' years')::interval)
Curious what others have used in the past as this is probably not the most efficient.
Good luck.