hlh3406
hlh3406

Reputation: 1398

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

Upvotes: 3

Views: 8652

Answers (1)

Air
Air

Reputation: 8615

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).

Upvotes: 5

Related Questions