Retrieve upcoming birthdays in Postgres - sql

I have a users table with a dob (date of birth) field, in a postgres database.
I want to write a query that will retrieve the next five upcoming birthdays. I think the following needs to be considered -
Sorting by date of birth won't work because the years can be different.
You want the result to be sorted by date/month, but starting from today. So, for example, yesterday's date would be the last row.
Ideally, I would like to do this without functions. Not a deal breaker though.
Similar questions have been asked on SO, but most don't even have an accepted answer. Thank you.

Well, this got downvoted a lot. But I'll post my answer anyway. One of the answers helped me arrive at the final solution, and that answer has been deleted by its owner for some reason.
Anyway, this query works perfectly. It gives the next 5 upcoming birthdays, along with the dates.
SELECT id, name,
CASE
WHEN dob2 < current_date THEN dob2 + interval '1 year'
ELSE dob2
END
AS birthday
FROM people,
make_date(extract(year from current_date)::int, extract(month from dob)::int, extract(day from dob)::int) as dob2
WHERE is_active = true
ORDER BY birthday
LIMIT 5;

You can look at day of year of dob and compare against current date's doy:
SELECT doy
, extract(doy from dob) - extract(doy from current_date) as upcoming_bday
FROM users
WHERE extract(doy from dob) - extract(doy from current_date) >= 0
order by 2
limit 5

Related

SQL : retrieve top 7 of entries added in the past week

I’m new to SQL and I need some help for a query that should return a top of occurrences sorted by date.
Actually I have a table to which are added searches done by users alongside the date of search (column is in the DATETIME format).
What I would like to do is to create a list of « Trending searches » that would show the top 7 searches done over the past week (Sunday to Sunday for example) but I’m unsure where to start.
I’ve heard of the DATEPART function but I don’t know how to use it alongside a top 7 occurrences.
Thanks in advance for your help and have a nice day !
Does this work?
declare #lastweek datetime
declare #now datetime
set #now = getdate()
set #lastweek = dateadd(day,-7,#now)
SELECT COUNT(g.searchTerm) AS appearanceCount, g.searchTerm FROM DesiredTable AS g
WHERE g.DateSearched BETWEEN #lastweek AND #now
GROUP BY(GameTypeId)
ORDER BY (appearanceCount) DESC
The mention of datepart() suggests SQL Server. If so, you can do:
select top (7) s.searchTerm, count(*)
from searches s
where s.searchTime >= dateadd(day, -7, getdate())
group by s.searchTerm
order by count(*) desc;
This gets the last 7 days to the current time.
If you want the last week, a pretty simple where is:
where datediff(week, s.searchTime, getdate()) = 1
This problem is solvable pretty easy with mysql. You are trying to do multiple things, as far as I understood:
1. Get searches from last week
With DATETIME fields there are pretty easy ways to extract the week of the year:
SELECT id FROM searches
WHERE searchDate >= curdate() - INTERVAL DAYOFWEEK(curdate())+6 DAY
AND searchDate < curdate() - INTERVAL DAYOFWEEK(curdate())-1 DAY
As suggested here.
2. Get the top 7 most frequent ones
Secondly you said that you want to have the top 7 searches, which translate into most frequent occurring search terms. In other words: You need to group identical search terms together and count them:
SELECT count(id), searchTerm FROM searches
WHERE searchDate >= curdate() - INTERVAL DAYOFWEEK(curdate())+6 DAY
AND searchDate < curdate() - INTERVAL DAYOFWEEK(curdate())-1 DAY
GROUP BY searchTerm
in addition:
To get the first n (here 7) rows use rownum<=7.
Like this (added to Gegenwinds solution):
SELECT result.* FROM
(SELECT count(id), searchTerm FROM searches
WHERE searchDate >= curdate() - INTERVAL DAYOFWEEK(curdate())+6 DAY
AND searchDate < curdate() - INTERVAL DAYOFWEEK(curdate())-1 DAY
GROUP BY searchTerm) result
ORDER BY count(id) DESC
WHERE rownum<= 7

SQL Date less than few days from today

I have a table in which I have emails to users and I want to make request to take users who are dated less than two days from today. How can make this?
This is my SQL table :
CREATE TABLE vacation_users(EMAIL VARCHAR(255), STARTDATE DATE, ENDDATE DATE);
The answer is this. I use HSQLDB and this is answer.
SELECT * FROM vacation_user WHERE (ENDDATE < (SYSDATE + 2 DAY));
Try this(not tested):
SELECT EMAIL from vacation_users
WHERE ENDDATE < DATEADD(day, +2, CURRENT_DATE) AND ENDDATE > CURRENT_DATE
It selects the mails, which have the enddate, which is equal bigger than current date, but smaller than current date + 2.
EDIT: I updated the answer, since the OP informed me about the DB, which is used. If CURRENT_DATE also doesn't work, you can try another from the built-in functions.
select *
from vacation_users
where STARTDATE = dateadd(day,datediff(day,2,STARTDATE),0)
That depends on a database you use, but the principle is more or less the same.
In Oracle, you'd do something like
select email
from vacation_users
where startdate < sysdate - 2;
which returns you to exact time 2 days ago (if it is 09:53 today, it'll be 09:53 2 days ago). Depending on what you really need, you might need to TRUNCate those values (so that you'd get date without its time component) or apply some other function or principle, regarding possible index impact etc.
Though, "dated less than two days from today" is ... what exactly? Is it a STARTDATE or ENDDATE (or some combination of those two)?
SELECT *
FROM vacation_users
WHERE (DATEADD(day,2,ENDDATE)) < (CONVERT(date, SYSDATETIME()))

How to SELECT number of hours worked in specifc weeks using postresql

I'm trying to find out how many hours employees worked on specific weeks in April and March.
When trying to find the total hours worked (numeric value), I'm given a "Date Out of Range" error. I'm fairly sure I made a simple mistake in my select queries. Could someone point out to me what's wrong?
How would you make a select query if you only wanted to get the number of hours worked from 2015-04-05 to 2015-04-11? If I can figure out how to get the numbers for that specified week, then that'll help me with the other weeks I need to look up as well. Pardon the newbish questions, I'm still new to SQL with very little experience.
SELECT
SUM(CASE WHEN hours_worked BETWEEN dateadd(ww,-1,'2015-04-05') AND dateadd(ww,-00,'2015-04-05') THEN r.hours_worked END) AS hours_worked
SUM(CASE WHEN hours_worked BETWEEN dateadd (wk,-1,'2015-3-14') AND dateadd(wk,-0,'2015-3-20') THEN r.hours_worked END) AS hours_worked
Here's one way to write a query similar to yours:
SELECT
name,
SUM(
CASE
WHEN hours_worked BETWEEN
(to_date('2015-04-05', 'YYYY-MM-DD') - interval '7 days')
AND to_date('2015-04-05', 'YYYY-MM-DD') THEN 1
ELSE 0
END
) AS number_of_records
from test
group by name
to_date function converts a string to a date.
- interval '7 days' tells PostgreSQL to go to last week.
Using hints from the query above, you should be able to suit it to your needs.

How to calculate ages in BigQuery?

I have two TIMESTAMP columns in my table: customer_birthday and purchase_date. I want to create a query to show the number of purchases by customer age, to create a chart.
But how do I calculate ages, in years, using BigQuery? In other words, how do I get the difference in years between two TIMESTAMPs? The age calculation cannot be made using days or hours, because of leap years, so the function DATEDIFF(<timestamp1>,<timestamp2>) is not appropriate.
Thanks.
First of all, I'd really love BigQuery to have a function which calculates current age based on a date. That seems to be like a very common use case and it's not really easy due to the whole leap year thing.
I found a great article about this issue: https://towardsdatascience.com/how-to-accurately-calculate-age-in-bigquery-999a8417e973
Their final approach is similar to Lars Haugseth's and Saad's answer, but they do not use the DAYOFYEAR part in order to avoid issues with leap years. It also gives you the flexibility not only to calculate the current age, but also the age at a particular date that you pass to the function as argument:
CREATE OR REPLACE FUNCTION workspace.age_calculation(as_of_date DATE, date_of_birth DATE)
AS (
DATE_DIFF(as_of_date,date_of_birth, YEAR) -
IF(EXTRACT(MONTH FROM date_of_birth)*100 + EXTRACT(DAY FROM date_of_birth) >
EXTRACT(MONTH FROM as_of_date)*100 + EXTRACT(DAY FROM as_of_date)
,1,0)
)
Regarding the difference between dates - you could consider user-defined functions (https://cloud.google.com/bigquery/user-defined-functions) with a JavaScript date library, such as Datejs or Moment.js
You can use DATE_DIFF to get the difference in years, but need to subtract by one if the birthday has not yet occured this year:
IF(EXTRACT(DAYOFYEAR FROM CURRENT_DATE) < EXTRACT(DAYOFYEAR FROM birthdate),
DATE_DIFF(CURRENT_DATE, birthdate, YEAR) - 1,
DATE_DIFF(CURRENT_DATE, birthdate, YEAR)) AS age
Here it is in a user defined function:
CREATE TEMP FUNCTION calculateAge(birthdate DATE) AS (
DATE_DIFF(CURRENT_DATE, birthdate, YEAR) +
IF(EXTRACT(DAYOFYEAR FROM CURRENT_DATE) < EXTRACT(DAYOFYEAR FROM birthdate), -1, 0) -- subtract 1 if bithdate has not yet occured this year
);
You can compute the number of days it would be if all years were 365 days long, take the difference, and divide by 365. For example:
SELECT (day2-day1)/365
FROM (
SELECT YEAR(t1) * 365 + DAYOFYEAR(t1) as day1,
YEAR(t2) * 365 + DAYOFYEAR(t2) as day2
FROM (
SELECT TIMESTAMP('20000201') as t1,
TIMESTAMP('20140201') as t2))
This returns 14.0, even though there are intervening leap years. If you want the final result as an integer instead of floating point, you can use the INTEGER() function to cast the result.
Note that if one of the dates is a leap day (feb 29) it will appear to be one year away from march 1, but I think this sounds like the intended behavior.
Another way to calculate age that takes leap years into account is to:
Calculate simple age based on difference in year
Either subtract 1 or not by:
Add difference in years to birthday (e.g. if today is 2022-12-14 and birthday is 2000-12-30, then the "new" birthday becomes 2022-12-30)
Do a DAY-based difference between today and "new" birthday, which either gives you a positive number (birthday passed for this year) or negative number (still has birthday this year)
Subtract 1 year from simple age calculation if number is negative
In BigQuery SQL code this looks like:
SELECT
bd AS birthday
,today
,DATE_DIFF(today, bd, YEAR) AS simpleAge
,DATE_DIFF(today, bd, YEAR) +
(CASE
WHEN DATE_DIFF(today, DATE_ADD(bd, INTERVAL DATE_DIFF(today, bd, YEAR) YEAR), DAY) >= 0
THEN 0
ELSE -1
END) AS age
FROM
(SELECT
PARSE_DATE("%Y-%m-%d", "2000-12-01") AS bd
,CURRENT_DATE("Asia/Tokyo") AS today
)
Outputs:
birthday
today
simpleAge
age
2000-12-30
2022-12-14
22
21

Question About SQL Query

I am using the below statement to generate age of a person in Oracle SQL and my question is below.
SELECT TO_NUMBER(TO_CHAR(CURRENT_DATE,'YYYY'))-TO_NUMBER(TO_CHAR(BIRTH_DATE,'YYYY'))
FROM NAME WHERE NAME_ID =NAME_ID
This statement is only correct upto so far that I need a statement which could count months and even days in order to get the age.
Googling for 'oracle get age from dob' returns several answers
select trunc((months_between(sysdate, dob))/12) age
from name;
looks like a good solution (trunc is optional) and
select to_number(to_char(sysdate,'YYYY')) - to_number(to_char(bth_date,'YYYY')) +
decode(sign(to_number(to_char(sysdate,'MMDD')) -
to_number(to_char(bth_date,'MMDD'))),-1,-1,0) age
from name;
is also correct.
You could use the EXTRACT function like
SELECT EXTRACT( YEAR FROM( CURRENT_DATE - BIRTH_DATE )) FROM ...
Substitute YEAR by whatever you need.
/edit I think I misread. If you need higher precisions maybe Intervals could help (http://blagispat.blogspot.com/2007/11/heres-short-article-on-using-intervals.html). (Sry but new users can only post one hyperlink).
SELECT EXTRACT( YEAR FROM( CURRENT_DATE - BIRTH_DATE) YEAR TO MONTH ) FROM ...
or
SELECT EXTRACT( DAY FROM( CURRENT_DATE - BIRTH_DATE) DAY TO SECOND ) FROM ...
which returns days.
Oracle supports arithmetic operations directly on DATE columns:
SELECT SYSDATE - BIRTH_DATE FROM NAME WHERE NAME_ID =NAME_ID
The result here will be a number which expresses the difference in days.
If you want it in months, use this:
SELECT MONTHS_BETWEEN(SYSDATE, BIRTH_DATE) FROM NAME...
If you want it in years, divide MONTHS_BETWEEN by 12.