I have some table with a datetime column, and I want to fetch from it all results like this:
If I execute the query on 2016-01-28 11:12:24, I want to fetch all results from 3 days ago from that whole hour (11:00:00). So I'd get all rows between 2016-01-28 11:00:00 and 2016-01-28 11:59:59. How do I do this, so that I can benefit from index on this datetime column?
I have tried using WHERE DATE_FORMAT(o.created_at, '%Y-%m-%d %H:00:00') = DATE_FORMAT(NOW() INTERVAL -3 DAY, '%Y-%m-%d %H:00:00') but it is a function, so the query doesn't use an index.
The best solution is to do this:
WHERE o.created_at >= DATE_FORMAT(date_sub(now(), interval 3 day), '%Y-%m-%d %H:00:00') AND o.created_at < DATE_FORMAT(date_add(date_sub(now(), interval 3 day), interval 1 hour), '%Y-%m-%d %H:00:00')
Alternately, you can also do this but only with precision up to seconds:
WHERE o.created_at BETWEEN DATE_FORMAT(date_sub(now(), interval 3 day), '%Y-%m-%d %H:00:00') AND DATE_FORMAT(date_sub(now(), interval 3 day), '%Y-%m-%d %H:59:59')
The BETWEEN AND clause here still utilizes the Index as we have not used DATE_FORMAT function on the column o.created_at. It is also much more efficient if the selected data is less than 1/3rd of the total rows.
[Ref: http://dev.mysql.com/doc/refman/5.0/en/mysql-indexes.html ]
Note: Thanks to #Used_By_Already for pointing out the issue with precision up to microseconds.
Instead of formating a new date with NOW() Interval - 3...., take a look at DateDiff(). This allows you to set the difference from NOW() to other days and set the measure unit.
Related
I want to select all rows between current day-1 with specific time 08:00am and to date now also with specific time. This is not working for example sample line code
WHERE SOME_DATE_COLUMN BETWEEN DATE_SUB(DATE(NOW() - INTERVAL 1 DAY) '08:00:00', '%Y-%m-%d %H:%i:%s') AND DATE_SUB(DATE(NOW()) '08:00:00', '%Y-%m-%d %H:%i:%s');
I tried also DATE_FORMAT
WHERE SOME_DATE_COLUMN BETWEEN DATE_FORMAT(DATE(NOW() - INTERVAL 1 DAY) '08:00:00', '%Y-%m-%d %H:%i:%s') AND DATE_FORMAT(DATE(NOW()) '08:00:00', '%Y-%m-%d %H:%i:%s');
If I run sql today I want to select all record between current date minus 1 day from 08:00am to current date 08:00am
I would do:
where
some_date_column >= current_date - interval 16 hour
and some_date_column < current_date + interval 8 hour
current_date gives you the current date (without the time part). You can then add and substract the required number of hours.
Note that this query does not use between, but instead half-open intervals. If you want consistent, adjacent segments in time, then you want to exclude one of the bounds (by convention, generally the outer bound is excluded).
Demo on DB Fiddle:
select current_date - interval 16 hour, current_date + interval 8 hour
current_date - interval 16 hour | current_date + interval 8 hour
:------------------------------ | :-----------------------------
2020-02-04 08:00:00 | 2020-02-05 08:00:00
I'm doing a review of existing code and have found the following SQL query which is used to get a selection of records last month.
Is there a more concise way of writing SQL to do what this date based clause does in MySQL?
SELECT foo
FROM some_table
WHERE some_date
BETWEEN
DATE_FORMAT(LAST_DAY((NOW() - INTERVAL 1 MONTH) - INTERVAL 1 SECOND), '%Y-%m-01 00:00:00')
AND
DATE_FORMAT(LAST_DAY((NOW() - INTERVAL 1 MONTH) - INTERVAL 1 SECOND), '%Y-%m-%d 23:59:59')
It works, but I just twitch a little every time I see it.
Can anyone else write it better?
Thank you in advance.
There's no need to format the dates, they default to YYYY-MM-DD 00:00:00.
This is a little bit simpler:
SELECT foo
FROM some_table
WHERE some_date >= LAST_DAY(CURDATE() - INTERVAL 2 MONTH) + INTERVAL 1 DAY
AND some_date < LAST_DAY(CURDATE() - INTERVAL 1 MONTH) + INTERVAL 1 DAY
So if CURDATE() is today, 2019-02-06, then:
- INTERVAL 2 MONTH is 2018-12-06
LAST_DAY() of that date is 2018-12-31
+ INTERVAL 1 DAY is 2019-01-01
Then the upper bound is:
- INTERVAL 1 MONTH is 2019-1-06
LAST_DAY() of that date is 2019-1-31
+ INTERVAL 1 DAY is 2019-02-01
The dates should be strictly less than 2019-02-01.
Using less than accounts for timestamps in the last second of the month, between 23:59:59.000 and 23:59:59.999.
I'm attempting to consolidate the where clause on the column name. I require a single column to return positive on the condition, but the same condition applies for many columns. The brute force example is below, to check for dates between a certain range on several columns. I have the columns OR'd with the exact same condition.
SELECT * FROM foo
WHERE foo.date1 BETWEEN DATE_SUB( DATE_ADD(DATE(NOW()), INTERVAL 16 HOUR), INTERVAL 24 HOUR) and DATE_ADD(DATE(NOW()), INTERVAL 16 HOUR)
OR foo.date2 BETWEEN DATE_SUB( DATE_ADD(DATE(NOW()), INTERVAL 16 HOUR), INTERVAL 24 HOUR) AND DATE_ADD(DATE(NOW()), INTERVAL 16 HOUR);
I would prefer to do something along the lines of:
SELECT * FROM foo
WHERE (foo.date1 OR foo.date2) BETWEEN DATE_SUB( DATE_ADD(DATE(NOW()), INTERVAL 16 HOUR), INTERVAL 24 HOUR) and DATE_ADD(DATE(NOW()), INTERVAL 16 HOUR);
.. but have it still work for individual columns across rows.
I'm using MariaDB, but can adapt any solutions from other flavors of sql.
The first works; the second is bad syntax.
I prefer
BETWEEN NOW() + INTERVAL 16-24 HOUR
AND NOW() + INTERVAL 16 HOUR
If the 16 is due to timezones, consider using TIMESTAMP and/or setting your system time appropriately.
What would be in my where clause if I was trying to limit my results to yesterday, from midnight to midnight. I know ">= NOW() - 1 Interval Day" returns the past 24 hours, but for a daily report, and wanting to look at the report any time besides midnight itself, how can I accomplish this?
Thank you!
You can use CURDATE():
where col >= date_sub(CURDATE(), interval 1 day) and
col < CURDATE()
http://sqlfiddle.com/#!9/431a2/3
SELECT *
FROM t1
WHERE t >= CURDATE();
SELECT
CONCAT(DATE_SUB(CURDATE(), INTERVAL 1 DAY), ' 00:00:00') AS start_date,
CONCAT(DATE_SUB(CURDATE(), INTERVAL 1 DAY), ' 23:59:59') AS end_date;
That should get you the dates you want, then put the concat(...) parts into your where clause.
I have a query that selects records created from 1 hour in past from current time.
select ts from <table_name> where ts >= DATE_SUB(NOW(), interval 1 hour);
I can also select date before 7 days using
select count(*) from <table_name> where ts >= DATE_SUB(NOW(), interval 7 day);
How can I use these two date features to get records before 7 days from today and time 1 hour in past from current time.
For example, if the present time is 2015-11-06 10:03:00 then how can I get data for time between 2015-10-30 09:03:00 to 2015-10-30 10:03:00
I tried something like this, but it gives syntax error:
select ts from <table_name> where ts >= DATE_SUB(DATE(NOW()), INTERVAL 7 DAY), interval 1 hour)
select ts from <table_name> where ts >= DATE_SUB(NOW(), INTERVAL 7 DAY), interval 1 hour)
Your examples have syntax errors (too many closing parentheses )). If you want to use DATE_SUB(), you need to use it twice. To get entries between one time and another, use WHERE ... BETWEEN ... AND ...
You can use this:
SELECT ts
FROM iv_split_skill_metrics
WHERE ts BETWEEN
DATE_SUB(
DATE_SUB(DATE(NOW()), INTERVAL 7 DAY),
interval 1 hour)
AND
DATE_SUB(DATE(NOW()), INTERVAL 7 DAY)
Or, even better, skip DATE_SUB() entirely and just do subtraction, like this:
SELECT ts
FROM iv_split_skill_metrics
WHERE ts BETWEEN NOW() - INTERVAL 7 DAY - INTERVAL 1 HOUR
AND NOW() - INTERVAL 7 DAY
Edit: For some reason, you edited your question after I posted this and replaced iv_split_skill_metrics with <table_name> in your question, but the examples above will work regardless. Just use the correct table and column names, of course!
Edit 2: I see now that you want entries between 7 days plus 1 hour ago and 7 days ago. I have tweaked my answer to show you how to do that.
Your goal is not 100% clear but just my attempt:
SELECT ts
FROM table_name
WHERE ts >= DATE_ADD(DATE_ADD(NOW(), INTERVAL -7 DAY), INTERVAL -1 HOUR)
AND ts <= DATE_ADD(NOW(), INTERVAL -7 DAY);
but form performance perspective this query would be much faster:
http://sqlfiddle.com/#!9/9edd1/2
SET #end = DATE_ADD(NOW(), INTERVAL -7 DAY);
SET #start = DATE_ADD(#end, INTERVAL -1 HOUR);
SELECT ts
FROM table_name
WHERE ts BETWEEN #start AND #end;