I have a table with anniversary dates. I want a query that returns me rows of anniversaries coming up in the next 10 days. For instance:
birthdate
---------
1965-10-10
1982-05-25
SELECT birthdate FROM Anniversaries WHERE mystical_magical_mumbo_jumbo <= 10
+------------+
| birthdate |
+------------+
| 1982-05-25 |
+------------+
1 row in set (0.01 sec)
I'd like to keep the query in the form x <= 10
, because I'll use that number 10 in other parts of the query, and if I set it to a variable, I can change it once everywhere by changing the variable, and not have to re-write the query.
Use the DATEDIFF() function to retrieve the number of days between two dates in a MySQL database.
To calculate the difference between the timestamps in MySQL, use the TIMESTAMPDIFF(unit, start, end) function. The unit argument can be MICROSECOND , SECOND , MINUTE , HOUR , DAY , WEEK , MONTH , QUARTER , or YEAR . To get the difference in seconds as we have done here, choose SECOND .
Use the YEAR() function to retrieve the year value from a date/datetime/timestamp column in MySQL. This function takes only one argument – a date or date and time. This can be the name of a date/datetime/timestamp column or an expression returning one of those data types.
As others have stated, you need to ignore the year in your comparison. The DAYOFYEAR() function is one way to do that.
Here's a quick solution off the top of my head. It will return all birthdays in the next 10 days, even if it's late December and the birthday is next year.
It DOES NOT handle leap years properly, so it will be off by 1 day for early March birthdays if this year is a leap year and the person was not born in a leap year or vice-versa. Leap years will also cause early January birthdays to show up one day off in late December sometimes. If anyone wants to add the leap year correction, feel free :)
SELECT birthdate
FROM Anniversaries
WHERE dayofyear(birthdate) - dayofyear(curdate()) between 0 and 10
or dayofyear(birthdate) + 365 - dayofyear(curdate()) between 0 and 10;
Try this
SELECT birthdate FROM Anniversaries
WHERE DATEDIFF(CURTIME(),birthdate) >= 0
AND DATEDIFF(CURTIME(),birthdate) <= 10
Check this for reference MySQL DATEDIFF
DATEDIFF(expr1,expr2)
DATEDIFF() returns expr1 – expr2 expressed as a value in days from one date to the other. expr1 and expr2 are date or date-and-time expressions. Only the date parts of the values are used in the calculation.
mysql> SELECT DATEDIFF('2007-12-31 23:59:59','2007-12-30'); -> 1
mysql> SELECT DATEDIFF('2010-11-30 23:59:59','2010-12-31'); -> -31
MAKEDATE()
creates a date from a year and the day of year, so I can use it to get old years out of the picture:
SELECT
anniversary
, MAKEDATE( YEAR(NOW()), DAYOFYEAR(anniversary) ) AS thisyear
FROM Anniversaries;
+-------------+-------------+
| anniversary | thisyear |
+-------------+-------------+
| 1978-07-29 | 2010-07-29 |
| 1959-04-17 | 2010-04-17 |
+-------------+-------------+
Then I can use DATEDIFF()
calculate the days until ( or from ) then:
SELECT
anniversary
, MAKEDATE( YEAR(NOW()), DAYOFYEAR(anniversary) ) AS thisyear
, DATEDIFF( MAKEDATE(YEAR(NOW()),DAYOFYEAR(anniversary)), NOW()) as days
FROM Anniversaries;
+-------------+-------------+------+
| anniversary | thisyear | days |
+-------------+-------------+------+
| 1978-07-29 | 2010-07-29 | 70 |
| 1959-04-17 | 2010-04-17 | -33 |
+-------------+-------------+------+
Assuming NOW() is 5/20.
Edit so the above doesn't work over year rollover. One solution is to add another calculation, where the anniversary is the next year. Here I've hard-coded the date '2010-12-31', and used an OR
in a HAVING
clause to filter by days that match either this year or next:
SELECT birth_date
, MAKEDATE(YEAR('2010-12-31'),DAYOFYEAR(birth_date)) as anniversary
, DATEDIFF( MAKEDATE(YEAR('2010-12-31'),DAYOFYEAR(birth_date)), '2010-12-31') as days
, MAKEDATE(YEAR(NOW())+ 1,DAYOFYEAR(birth_date)) as next_anniversary
, DATEDIFF( MAKEDATE(YEAR(NOW())+ 1,DAYOFYEAR(birth_date)), '2010-12-31') as next_days
FROM Anniversaries
HAVING ( ( days <= 25 AND days > 0 ) OR next_days <= 25 );
+------------+-------------+------+------------------+-----------+
| birth_date | anniversary | days | next_anniversary | next_days |
+------------+-------------+------+------------------+-----------+
| 2010-01-23 | 2010-01-23 | -342 | 2011-01-23 | 23 |
| 1975-01-11 | 2010-01-11 | -354 | 2011-01-11 | 11 |
+------------+-------------+------+------------------+-----------+
2 rows in set (0.00 sec)
What we want to achieve is a list of anniversaries occurring in a recent date range. To achieve this and to accommodate rollover we need to generate a list of all anniversaries that cover the date range. The solution that I have used, generates anniversary records for last year, this year and next year and outputs only those records that fall within the given date range. The union query looks like this:
SELECT Date_Add(birth_date,INTERVAL (Year(now())-Year(birth_date)-1) YEAR) as dt,
FROM Anniversaries
WHERE Date_Add(birth_date,INTERVAL (Year(now())-Year(Date)-1) YEAR)
BETWEEN Date_sub(now(), INTERVAL 7 Day) AND Date_add(now(), INTERVAL 21 Day)
UNION
SELECT Date_Add(birth_date,INTERVAL (Year(now())-Year(birth_date)) YEAR) as dt,
FROM Anniversaries
WHERE Date_Add(birth_date,INTERVAL (Year(now())-Year(Date)) YEAR)
BETWEEN Date_sub(now(), INTERVAL 7 Day) AND Date_add(now(), INTERVAL 21 Day)
UNION
SELECT Date_Add(birth_date,INTERVAL (Year(now())-Year(birth_date)+1) YEAR) as dt,
FROM Anniversaries
WHERE Date_Add(birth_date,INTERVAL (Year(now())-Year(Date)+1) YEAR)
BETWEEN Date_sub(now(), INTERVAL 7 Day) AND Date_add(now(), INTERVAL 21 Day)
ORDER BY dt
The first SELECT statement increments all the dates to last year and selects any items that fall into the date range provided The next SELECT statement increments all the dates to the current year and selects any items that fall into the date range provided The last SELECT statement increments all the dates to next year and selects any items that fall into the date range provided Finally it is all ordered by date.
There you have it. This will reliably select anniversaries in the given period. If a longer period is required, it may be necessary to generate additional SELECT statements and join them with UNION. I have modified my code to match the example above but have not tested it since modification.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With