Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MySQL Time interval on a query

I want to run a cron job through my website, that will pick up all email addresses that haven't been active from 2 months and 1 day.

I've seen this post:

How to select last 6 months from news table using MySQL

And so I know I want to do something like this:

SELECT DISTINCT email FROM orders WHERE date = DATE_SUB(now(), INTERVAL 2 MONTH)

The additional day is what has me a little stuck - do I have to specify a number of days like this:

SELECT DISTINCT email FROM orders WHERE date = DATE_SUB(now(), INTERVAL 63 DAYS)

But then I'm concerned about months with different lengths in them (29, 30, 31), so is it also a case of having to write a small script to check on the number of days in the month before setting the interval?

Possibly I'm making this too complicated so any help would be appreciated!

Thanks

like image 209
hlh3406 Avatar asked May 07 '26 21:05

hlh3406


1 Answers

You can only use one type at a time with INTERVAL. There are several "hybrids" like HOUR_SECOND or YEAR_MONTH but there doesn't seem to be any MONTH_DAY hybrid type available that would be used for an interval of j months and k days.

That being said, the DATE_ADD and DATE_SUB functions exist to make simple date arithmetic more readable. You can still use date arithmetic in its more basic, but less readable form—take a look at what happens when you run the following queries:

CREATE TEMPORARY TABLE foo (SELECT NOW() a, NOW()+1 b, NOW()+10000000000001 c);

SELECT * FROM foo;

DESCRIBE foo;

Don't squint too long at the second query before you try running the third. What's going on here is that MySQL can represent a DATETIME in a few ways, including as a string: 'YYYY-MM-DD hh:mm:ss' or, equivalently, as a double-precision numeric value: YYYYMMDDhhmmss.ffffff where the f represents a decimal fraction of a second to six places (microsecond resolution).

Understanding the above, the naive solution might be:

SELECT NOW() - 00000201000000.000000
             # YYYYMMDDhhmmss.ffffff   (format of above)

However, even though you're representing a datetime, you're still using a number, so you don't need all those extra leading zeros or empty decimal places. You can do this instead:

SELECT NOW() - 201000000
             # MDDhhmmss   (format of above)

Finally, you can CAST this numerical result back to the DATETIME format for use in your query:

SELECT DISTINCT email FROM orders WHERE date = CAST(NOW() - 201000000 AS DATETIME)

There are dozens of other ways to do this. My preference would probably be the following, for readability:

SELECT DISTINCT email FROM orders WHERE date = ((NOW() - INTERVAL 2 MONTH) - INTERVAL 1 DAY)

The grouping in the arithmetic expression is not required, but I'd recommend using at least the outer set of parentheses to emphasize that both subtractions are intended. Personally, if I saw NOW() - INTERVAL 2 MONTH - INTERVAL 1 DAY I might wonder if someone had intended to change from one interval to the other and simply forgotten to delete a bit of code. But the style choice is yours (and the most explicit thing would be to comment your code anyway).

like image 84
Air Avatar answered May 11 '26 10:05

Air



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!