Reputation: 864
I currently have a query that is getting the records where their deadline
is less than 3 months away. The deadline
is stored as a Date, but the year is not important as this record should flag up every year.
My query:
SELECT client_name
FROM client
WHERE MOD(DAYOFYEAR(deadline) - DAYOFYEAR(CURDATE()), +365) <= 90
AND DAYOFYEAR(deadline) > DAYOFYEAR(CURDATE())
Apologies if there’s errors in the syntax as I’m writing this from memory – but it does work.
It works up until a deadline
is in the first quarter and the current date is in the final quarter then it no longer returns the record. How do I get around this?
So the query needs to return the records that have a deadline within 3 months of the current date. The Year in the deadline
date should be ignored as this could be years ago, but it is the day and month of the deadline
that is important.
Or is the problem the date I am storing? Should I update this each year?
Thanks
Upvotes: 0
Views: 524
Reputation: 108430
One approach is to use a conditional test like this:
WHERE CONCAT(YEAR(NOW()),DATE_FORMAT(d.deadline,'-%m-%d'))
+ INTERVAL CONCAT(YEAR(NOW()),DATE_FORMAT(d.deadline,'-%m-%d'))<NOW() YEAR
< NOW() + INTERVAL 3 MONTH
We can unpack that a little bit. On that first line, we're creating a "next due" deadline date, by taking the current year, and appending the month and day value from the deadline
.
But there's a problem. Some of those "next due" deadline dates are in the past. So, to handle that problem (when the 3 month period "wraps" into the next year), we need to add a year to any "next due" deadline date that's before the current date.
Now, we can compare that to a date 3 months from now, to determine if the "next due" deadline date is in the next 3 months.
That's a bit complicated.
Here's a SQL Fiddle as a demonstration: http://sqlfiddle.com/#!2/c90e9/3.
For testing, NOW()
is inconvenient because it always returns today's date. So, for testing, we replace all occurrences of NOW()
with a user-defined variable @now
, and set that to various dates, so we can appropriately test.
Here's the SQL statement I used for testing. The first expression is the conditional test we're planning on using in the WHERE
clause. For testing, we want to return all the rows, and just see which rows get due_in_3mo
flagged as TRUE
(1
) and which get flagged as FALSE
(0
).
The second expression in the SELECT list just the "next due" deadline date, same as used in the first expression.
The rest of the expressions are pretty self-explanatory... we also want to display the date 3 months in the future we're comparing to, and the original "deadline" date value.
SELECT
CONCAT(YEAR(@now),DATE_FORMAT(d.deadline,'-%m-%d'))
+ INTERVAL CONCAT(YEAR(@now),DATE_FORMAT(d.deadline,'-%m-%d'))<@now YEAR
< @now + INTERVAL 3 MONTH
AS `due_in_3mo`
, CONCAT(YEAR(@now),DATE_FORMAT(d.deadline,'-%m-%d'))
+ INTERVAL CONCAT(YEAR(@now),DATE_FORMAT(d.deadline,'-%m-%d'))<@now YEAR
AS `next_due`
, d.id
, d.deadline + INTERVAL 0 DAY AS `deadline`
, @now + INTERVAL 3 MONTH AS `now+3mo`
, @now + INTERVAL 0 DAY AS `now`
FROM d d
CROSS
JOIN (SELECT @now := '2015-11-01') i
ORDER BY d.id
Change the value assigned to @now
in the inline view (aliased as i
) to test with other date values.
(You may want to use DATE(NOW())
in place of NOW()
so that times don't get mixed in, and you may want to subtract another day from that, that really just depends how you want to handle the edge case of a deadline
with month and day the same as the current date. (i.e. do you want to handle that as "in the past" or not.)
To summarize the approach: generate the "next due" deadline date as a DATE value in the future, and compare to the a date 3 months from now.
Upvotes: 2