Upcoming birthdays year bug (SQL) - mysql

this is my current sql query that gets all the upcoming birthdays for my company in the next 90 days:
SELECT
user.birthday, user.name, MONTH(user.birthday)
AS month, DAY(user.birthday) AS day
FROM user WHERE
(1 =
(FLOOR(DATEDIFF(DATE_ADD(DATE(NOW()),INTERVAL
90 DAY),birthday) / 365.25)) -
(FLOOR(DATEDIFF(DATE(NOW()),birthday)
/ 365.25)))
ORDER BY MONTH(birthday),DAY(birthday)
The problem, is that if right now is november, and there are some birthdays in january, it will display january birthdays first, then november and then december, although january birthdays already happened THIS year.
Is there a way to reorder this records in the same SQL query, so that it displays current and future months first, and THEN next year's months?
First partial solution thanks to Johan
ORDER BY ( MONTH(birthday) > MONTH(NOW()
OR ((MONTH(birthday) = MONTH(now())
AND DAY(birthday) >= DAY(NOW()) DESC
, MONTH(birthday), DAY(birthday)
Still it needs a little improvement. If a birthday already happened, it should be displayed AFTER december on the results. Example of what should be displayed assuming it is 27th june
28 june: john doe
27 december: mary wright
5 june (next year of course): mad max

I'm not sure but it seems that your birthday includes the year. If that's so than you'll have a range of birthdays per user (one for every year) and you can just select the ones within the next 90 days.
SELECT
user.birthday
, user.name
, MONTH(user.birthday) AS month
, DAY(user.birthday) AS day
FROM user
WHERE birthday BETWEEN NOW() AND DATE_ADD(NOW, INTERVAL 90 DAY)
ORDER BY Birthday DESC
If your birthday only has a month and day, your query needs to be:
SELECT
user.birthday
, user.name
, MONTH(user.birthday) AS month
, DAY(user.birthday) AS day
FROM user
WHERE STR_TO_DATE(CONCAT(YEAR(NOW()),MONTH(birthday),DAY(birthday)),'%YYYY%M%D')
BETWEEN NOW() AND DATE_ADD(NOW(), INTERVAL 90 DAY) OR
STR_TO_DATE(CONCAT(YEAR(DATE_ADD(NOW(),INTERVAL 1 YEAR)),MONTH(birthday),DAY(birthday)),'%YYYY%M%D')
BETWEEN NOW() AND DATE_ADD(NOW(), INTERVAL 90 DAY)
ORDER BY ( MONTH(birthday) > MONTH(NOW()
OR ((MONTH(birthday) = MONTH(now()) AND DAY(birthday) >= DAY(NOW()) DESC,
MONTH(birthday), DAY(birthday)

I believe you need to order using something that includes the year.
ORDER by date_format( date, "%d/%m/%Y" )
I am no expert but something like this may work too.
ORDER BY YEAR(birthday),MONTH(birthday),DAY(birthday)

I think you want to know if each user's birthday, brought in to the current year or the next year, falls between your range:
SELECT name, birthday
FROM (SELECT name, birthday, YEAR(NOW()) - YEAR(birthday) AS years_ago
FROM user) d
WHERE DATE_ADD(birthday, INTERVAL years_ago YEAR)
BETWEEN NOW() AND DATE_ADD(NOW(), INTERVAL 90 DAY)
OR
DATE_ADD(birthday, INTERVAL (years_ago + 1) YEAR)
BETWEEN NOW() AND DATE_ADD(NOW(), INTERVAL 90 DAY);
(It occurs to me that you might actually want INTERVAL 3 MONTH, rather than 90 DAY, expecially if you intend to run this query on the first of every month.)

Your query will create a full table scan.
Store an integer containing the day of the year (1st of april is going to be around 90), and compare that with the current day of the year.

I've been searching for this code, but I couldn't find a clean/simple query (that also works with leap-years (29th of february problem))
So i've made my own.
Here's the simplest code to get the upcoming birthdays for the next x days, (this query also displays the birthdays of yesterday (or you can change it to a x number of days in the past)
SELECT name, date_of_birty
FROM users
WHERE DATE(CONCAT(YEAR(CURDATE()), RIGHT(date_of_birty, 6)))
BETWEEN
DATE_SUB(CURDATE(), INTERVAL 1 DAY)
AND
DATE_ADD(CURDATE(), INTERVAL 5 DAY)

Related

How to show current months projects in list, where current month date is between A and B on new year roll over

Last year I set up a project management system, but now as we're on a new year I've been bit in my ass by some newbie coding (of me) :)
So now I really need your help to understand how to fix this in a good way once and for all.
The error:
The major problem right now is in the year roll over, right now my SQL query dosn't understand that the month of the new year (1) is larger than the current years (12) which then dosn't show the right projects in the list.
Any ideas? Thanks in advance!
This is my current SQL Query:
SELECT *
FROM projects
WHERE MONTH(CURDATE()) between MONTH(project_start) and MONTH(project_delivery)
AND YEAR(CURDATE()) between YEAR(project_start) and YEAR(project_delivery)
order by project_id
This is the raw structure of the table projects:
Project_id projet_start projet_delivery
1 2018-12-20 2018-12-22
2 2018-12-25 2018-12-29
3 2018-12-28 2018-12-28
4 2018-12-30 2019-01-22
BETWEEN works for actual dates:
SELECT *
FROM projects
WHERE CURDATE() between project_start and project_delivery
order by project_id
If you wanted everything from the month (month granularity) rather than day granularity:
SELECT *
FROM projects
WHERE CURDATE() between DATE_SUB(project_start, INTERVAL DAY(project_start)-1 DAY) and
DATE_ADD(DATE_SUB(project_delivery, INTERVAL DAY(project_delivery) DAY), INTERVAL 1 MONTH)
order by project_id
So if a project started on Dec 5th and finished on Jan 19th, this would give everything between Dec 1st and Jan 31st. Do bear in mind though that if any date on Jan 31st also has a time component (i.e. later than midnight), it will mean it's fractionally after this end date and won't show.
Comment if that's the case and you want help solving it (easiest to not use BETWEEN because it's always inclusive at each end, use < which is exclusive)
Update: Projects that have had some activity this month, i.e a project that:
started before, ended during
started before, ended after
started during, ended during
started during, ended after
The common thing all these have is that the start date of the project is before the end of this month, and also the end date of the project is after the first of this month
SELECT *
FROM projects
WHERE
--started before the end of this month
project_start < DATE_ADD(DATE_SUB(CURDATE(), INTERVAL DAY(CURDATE()) - 1 DAY), INTERVAL 1 MONTH) AND
--ended after the start of this month
project_delivery > DATE_SUB(CURDATE(), INTERVAL DAY(CURDATE()) DAY)
order by project_id
Doing a DATE_SUB(CURDATE(), INTERVAL DAY(CURDATE()) DAY) is a rather convoluted way of writing "subtract the current day number from the current date" i.e. 2019-01-02 minus 2 -> 2018-12-31. We look for dates > this (so as not to include it)
Similarly, DATE_ADD(DATE_SUB(CURDATE(), INTERVAL DAY(CURDATE()) - 1 DAY), INTERVAL 1 MONTH) Takes the current date, subs DAY-1 (so just 1 this time, not 2) off it to reach 1st of this month, then adds a month on to get to first of next month. 2019-01-02 -> 2019-01-01 -> 2019-02-01. Again < it so it's exclusive
There's a bug in going the other way (add a month then sub the day) if you're on eg the 31st of jan and you add a month first - there is no 31st of feb so mysql will cap at 28th of feb, then sub 31 days off it, giving a date that is not the end of jan (i.e. 28th of jan)
Just use date comparisons:
select p.*
from projects p
where curdate() >= project_start and
curdate() <= project_delivery;
I'm not sure why you would want to break the dates into their time components. However, that is totally unnecessary to compare them.
If you want to just do the comparison at the month level, then one method is to convert the values to months:
select p.*
from projects p
where year(curdate()) * 12 + month(curdate()) >= year(project_start) * 12 + month(project_start) and
year(curdate()) * 12 + month(curdate()) <= year(project_delivery) * 12 + month(project_delivery);
Alternatively, just move the dates to the beginning of months:
select p.*
from projects p
where curdate() >= project_start + interval 1 - day(project_start) day and
curdate() < ( project_delivery + interval (1 - day(project_delivery) day) + interval 1 month;

Select next 7 days using only day and month

I have a table with birthdays in them, formatted in YYYY-MM-DD. My aim is to return results if the birthday is within the next 7 days.
I need it to use only the month and day, because if it reads the year also, a birthday in 1993 is never going to be within the next 7 days. It also needs take month changes into consideration.
For example, if its the 28th of Feb, and a birthday in the table is on the 1st March, that would be within 7 days, but not within the same month.
SELECT * FROM user WHERE DATE_FORMAT(birthday, '%m-%d') >= DATE_FORMAT(NOW() - INTERVAL 7 DAY, '%m-%d')
This is where i'm at, at the moment, but I know i'm miles off.
You need to format both of your dates in the WHERE clause.
SELECT *
FROM user
WHERE DATE_FORMAT(birthday, '%m-%d') BETWEEN DATE_FORMAT(NOW(), '%m-%d') AND DATE_FORMAT(DATE_ADD(NOW(), INTERVAL 7 DAY), '%m-%d')
retrieve current year with YEAR(CURDATE(), make a date biy concatenating with month and day coming from birthday and remove 7 days, check if curdate is in the range :
select *
from user
where
Curdate() between date_sub(CONCAT(YEAR(CURDATE()),'-', date_format(birthday, '%m-%d')), interval 7 day)
and CONCAT(YEAR(CURDATE()),'-', date_format(birthday, '%m-%d')) ;
Take year part from today's date and concatenate it with the month and day part of the dob column value. And cast that string to date. Then use that as a sub-query and check whether thet new column day difference is between 0 and 7.
Query
SELECT t.* -- select except the `new_col`
FROM (SELECT *,
CAST((Concat(YEAR(NOW()), '-', DATE_FORMAT(dob, '%m-%d'))) AS DATE
) AS
`new_col`
FROM `user`)t
WHERE DATEDIFF(t.`new_col`, NOW()) BETWEEN 0 AND 7;

Mysql date_sub interval 12 month

I'm trying to get all posts from the 12 last month, group by month. I have a quite correct query:
SELECT MONTH(time) as mois, YEAR(time) as annee, count(*) as nbre
FROM touist_stories
WHERE time >= DATE_SUB(now() + INTERVAL 1 MONTH, INTERVAL 2 YEAR)
group by MONTH(time)
order by YEAR(time) DESC, MONTH(time) DESC
But one month is always missing : november 2012
I tryied to add
+ INTERVAL 1 MONTH
to now() but it still missing... How can I get the 12 last month and not the 11 ones please?
Thanks
To get one year ago, here's a technique I've used in the past. Using #mysql variables, create a date based on the first day of a given month/year (via now()), then subtract 12 months. This example will get from Oct 1, 2012 to current -- which will include current Oct 2013. To exclude that, just add to where clause where I re-added 1 year so it goes from Oct 1, 2012 at 12:00:00 am to LESS THEN Oct 1, 2013 12:00:00.
SELECT
MONTH(time) as mois,
YEAR(time) as annee,
count(*) as nbre
FROM
touist_stories,
( select #lastYear := date_add( DATE_FORMAT(NOW(),
'%Y-%m-01'), interval -11 month) ) sqlvar
WHERE
time >= #lastYear
group by
MONTH(time)
order by
YEAR(time) DESC,
MONTH(time) DESC
Revised to make it go 11 months back (to November per example), and include UP TO AND INCLUDING all Current October activity.
For realy want on year data use 11 MONTH not 12
SELECT time
FROM touist_stories
WHERE time
BETWEEN
date_sub(Now(), INTERVAL 11 MONTH)
AND
Now();

How to SELECT last calender month (not the interval)?

I have date time field called transaction_date, in a report i need to select last calendar month, how do i do this ? (this should work for a month like January too)
I came up with following but this only works if the month is NOT january,
SELECT SUM(amount) AS pay_month FROM `users_payment` WHERE MONTH(transaction_datetime)= MONTH(NOW()) AND YEAR(transaction_datetime)=YEAR(NOW())
there are lot of examples using INTERVAL functions but this only select the time interval not the calendar month as i wanted too..
like
SELECT SUM(amount) AS `year_month` FROM `users_payment` WHERE DATE_ADD(NOW(), INTERVAL -1 MONTH) < transaction_datetime
but this is not what i want, i want to select sales sum of the DECEMBER only last year (remember there are other years too in the table which i dont want i.e 1979, 1981...etc)
same report next section, i need to select last 2 calender months, I dont know have any idea on how to do this too.
Have you tried the following
SELECT SUM(amount) AS `year_month` FROM `users_payment`
WHERE MONTH(DATE_ADD(NOW(), INTERVAL -1 MONTH)) = MONTH(transaction_datetime)
The above should work to show previous month; it does not distinguish between years however.
On second thought, I see what you are trying to do - To get all the transactions for a given month. Try something like this instead.
SELECT SUM(amount) AS `year_month` FROM `users_payment`
WHERE transaction_datetime BETWEEN date_format(NOW() - INTERVAL 1 MONTH, '%Y-%m-01')
AND last_day(NOW() - INTERVAL 1 MONTH)
This will list all the transactions for the previous calendar month. Alter the INTERVAL values to select multiple months.
You can try this--
SELECT SUM(amount) AS pay_month FROM `users_payment` WHERE
PERIOD_ADD(DATE_FORMAT(NOW(),'%Y%m'), -1) = DATE_FORMAT(transaction_datetime,'%Y%m')

MySQL: receive data only per months

Few times ago, I asked how to do to display data per month, I must told a bad explanation because I just figured out that it's not what I want :
Here's what I got :
$req1 = ...
AND v.date > (DATE_SUB(CURDATE(), INTERVAL 2 MONTH))
AND v.date < (DATE_SUB(CURDATE(), INTERVAL 1 MONTH))
$req2= ...
AND v.date > (DATE_SUB(CURDATE(), INTERVAL 3 MONTH))
AND v.date < (DATE_SUB(CURDATE(), INTERVAL 2 MONTH))
But the problem, imagine that today you are the 10th June, it's going to calculate ALL the data between the
10 june to the 10 may
then the 10 may until the 10 april...
But what I want is data :
from 1st may to 1 st june,
from 1st june to 1st july...
Do you see what I mean ?
You could use:
WHERE YEAR(date) = 2010 AND MONTH(date) = 5
to get all rows where date is in the YEAR 2010 and the fifth month of the year.
AND MONTH(v.date)=6 AND YEAR(v.date)=2009 [to get everything in June 2009]
select month(v.date), year(v.date), sum(somedatacolumn) from thetable group by month(v.date), year(v.date);
replace sum with whatever calculation you are doing