Select where unix timestamp falls on specified hour or day - mysql

I'm trying to build a dynamic where for a MySQL query. The user can select how many hours or days ago they want data for. I want the data to fall on the particular hours or days ago.
Here is the code I'm using right now which does not work:
$condition['dates'] = 'Specific';
$condition['date_operand'] = 'Hour(s) ago';
$condition['date_value'] = '3';
if ($conditions['dates'] == 'Specific' && !empty($conditions['date_value'])) {
if ($conditions['date_operand'] == 'Hour(s) ago') {
$where[] = "date_format(from_unixtime(l.date_updated), '%Y-%m-%d %H') = date_format(now() - interval ".$conditions['date_value']." hour, '%Y-%m-%d %H')";
}
else if ($conditions['date_operand'] == 'Day(s) ago') {
$where[] = "date_format(from_unixtime(l.date_updated), '%Y-%m-%d') = date_format(now() - interval ".$conditions['date_value']." day, '%Y-%m-%d')";
}
}
It does not seem to be working whatsoever. The l.date_updated is a unix timestamp. You can see what I'm trying to achieve, it just isn't working.
UPDATE
Here is the MySQL where statement from the example which is not working:
SELECT * FROM mytable l
WHERE DATE_FORMAT(FROM_UNIXTIME(l.date_updated), '%Y-%m-%d %H') = DATE_FORMAT(NOW() - INTERVAL 3 HOUR, '%Y-%m-%d %H')
It does not cause an error, it simply isn't selecting what I want.
FINAL UPDATE
My query was actually fine. It appears my PHP code was connecting to a development database which hasn't been updated in years. That's why when I selected 3 days ago it returned zero rows.
So please, a little respect, for I am Dion, lord of the idiots.

It seems to me you want to choose rows where your date_updated value lies in a range of time. For example, I think you mean, if NOW() is 2017-04-03 17:55:22, you want all the records timestamped between 2017-04-03 14:00:00 and 2017-04-03 14:59:59.99999 inclusive.
Here's how you do that in a sargable way: a way that can use an index on your date_updated column.
This expression truncates NOW() to the top of the hour: 17:55 to 17:00:
DATE_FORMAT(NOW(), '%Y-%m-%d %H:00:00')
This backs up three hours.
DATE_FORMAT(NOW(), '%Y-%m-%d %H:00:00') - INTERVAL 3 HOUR
The beginning of your timestamp range is:
UNIX_TIMESTAMP(DATE_FORMAT(NOW(), '%Y-%m-%d %H:00:00') - INTERVAL 3 HOUR)
The end of your timestamp range, then, is
UNIX_TIMESTAMP(DATE_FORMAT(NOW(), '%Y-%m-%d %H:00:00') - INTERVAL 2 HOUR)
So, this WHERE clause does the trick.
WHERE l.date_updated >= UNIX_TIMESTAMP(DATE_FORMAT(NOW(),'%Y-%m-%d %H:00:00')-INTERVAL 3 HOUR)
AND l.date_updated) < UNIX_TIMESTAMP(DATE_FORMAT(NOW(),'%Y-%m-%d %H:00:00')-INTERVAL 2 HOUR)
Notice the <, not <=, at the end of the time range.
There's some time zone stuff happening here. Unix timestamps are (or should be) always recorded with respect to UTC. The UNIX_TIMESTAMP() function always converts from local time to UTC, and the NOW() function always works in local time, so this all should work properly. But you might investigate all this timestamp junk if you're still getting the wrong rows, or no rows.
Please notice that your time precision would be the same and your life would be easier if your column had the TIMESTAMP data type rather than the INT data type.

you can use TO_SECONDS:
https://dev.mysql.com/doc/refman/5.5/en/date-and-time-functions.html#function_to-seconds
... where TO_SECONDS(l.date_updated) >= TO_SECONDS(NOW()-INTERVAL 3 HOUR)

Related

mysql BETWEEN date range not working

I have a table called barcode_log, and these are all the datas from the table.
And now if I run this query
SELECT * FROM `barcode_log` WHERE barcode_log.assign_time BETWEEN DATE_SUB(CURRENT_DATE, INTERVAL 30 DAY) AND CURRENT_DATE;
I get this result
But it should return all the rows as all the data is within this month only. And assign_time field is stored as datetime. Any idea what i am doing wrong??
You are ignoring the time part (hh:mm:ss).
If the end day is set to the end timestamp of the current date then you can get the data of current day's too.
BETWEEN is inclusive
SELECT
*
FROM
`barcode_log`
WHERE
barcode_log.assign_time BETWEEN DATE_SUB(
CURRENT_DATE,
INTERVAL 30 DAY
)
AND TIMESTAMP(CONCAT(CURDATE(),' ','23:59:59'));
While the accepted answer works, there is a simpler solution. Just take the date part of the datetime column:
SELECT
*
FROM
`barcode_log`
WHERE
DATE(barcode_log.assign_time)
BETWEEN DATE_SUB(CURRENT_DATE, INTERVAL 30 DAY) AND CURRENT_DATE;
There is another way around: CAST() on barcode_log.assign_time field.
SELECT *
FROM `barcode_log`
WHERE CAST(barcode_log.assign_time AS DATE)
BETWEEN DATE_SUB(CURRENT_DATE, INTERVAL 30 DAY) AND CURRENT_DATE;
This excludes time from comparison and works fine for your purpose.

Select all MySQL records in the last day that are between a time range of 7am and midnight

I have this query
SELECT * FROM `timeclock_timecard`
WHERE `clock_in_datetime` > DATE_SUB(CURDATE(), INTERVAL 1 DAY)
which can get record in the last day but I need to limit to records created after 7AM
Any help please?
SELECT * FROM `timeclock_timecard`
WHERE `clock_in_datetime` > DATE_SUB(CURDATE(), INTERVAL 1 DAY)
and hour(`clock_in_datetime`) > 7;
Added one more filter condition to check for the hour.
Your query was almost correct, because CURDATE() only gives the date you can just subtract 17 hours to get the correct result. fiddle.
SELECT * FROM `timeclock_timecard`
WHERE `clock_in_datetime` >= DATE_SUB(CURDATE(), INTERVAL 17 HOUR)
To get the entries of the current day, we can add 7 hours (CURDATE() has time 0:00).
SELECT * FROM `timeclock_timecard`
WHERE `clock_in_datetime` >= DATE_ADD(CURDATE(), INTERVAL 7 HOUR)
To get only rows from yesterday, with a time value of 7AM or later, we can add 7 hours to the expression.
If we only up until midnight of today (just rows from yesterday), we can add another condition, the datetime is less than midnight today.
For example:
SELECT t.*
FROM `timeclock_timecard` t
WHERE t.`clock_in_datetime` >= DATE(NOW()) + INTERVAL -1 DAY + INTERVAL 7 HOUR
AND t.`clock_in_datetime` < DATE(NOW())
If you want to exclude the exact 7:00:00 AM value, change the >= to just >.
FOLLOWUP
Q: What I actually want is between about 5-6am TODAY and mindnight TODAY so anytime during today that I run the report for today I will get only timeclock data from users who clocked in/out today only and not include yesterdays data.
A: The predicates are going to be of the form
WHERE t.`clock_in_datetime` >= expr1
AND t.`clock_in_datetime` < expr2
You just need to find the expressions expr1 and expr2 that return the appropriate datetime values.
Just use a simple SELECT statement to test:
SELECT DATE(NOW()) + INTERVAL 5 HOUR AS `start`
, DATE(NOW()) + INTERVAL 1 DAY AS `end`
Q: I also modified my select to take in account my datetime is in UTC and my result needs to get todays records using local timezone.
SELECT * , CONVERT_TZ( clock_in_datetime , '+00:00', '-4:00' ) FROM `timeclock_timecard`
A: Personally, I would do the timezone conversion on the exprN values, not the column values. Having predicates on bare columns allows MySQL to make effective use of an index; wrapping the columns in expressions prevents MySQL from using an index.
If the MySQL system clock is UTC, and your datetime values stored in the table are in a different timezone, yes, use the MySQL CONVERT_TZ function.
Again, using a simple SELECT statement to develop and test the expressions:
SELECT CONVERT_TZ( DATE(NOW()) + INTERVAL 5 HOUR, '+0:00', to_tz) AS `start`
, CONVERT_TZ( DATE(NOW()) + INTERVAL 1 DAY , '+0:00', to_tz) AS `end`
Where to_tz is the timezone of the values in the table.
Once you get expressions start and end returning the values you need, then use those expressions in the predicates of the query of the timecard table.

Trouble with date ranges

I need to check for entries made in the last "x" days (example 30 days) and cannot get the query to work. This is what I am using:
SELECT CAL_OWNER,
CAL_TITLE,
FROM_UNIXTIME (CAL_CREATED, "%m-%d-%y") AS CREATED,
FROM_UNIXTIME (RANGE_START, "%Y-%m-%d") AS DATE2BESEEN,
CASE CAL_REFERRAL_TYPE
WHEN 1 THEN 'NoReferral'
WHEN 2 THEN 'CareyGuide'
WHEN 3 THEN 'Education'
WHEN 4 THEN 'Employment'
WHEN 5 THEN 'Housing'
WHEN 6 THEN 'Medical'
ELSE 'NA'
END
AS REFERRALS
FROM EGW_CAL
WHERE CAL_CREATED BETWEEN (NOW () - '30 day') AND NOW ()
ORDER BY REFERRALS ASC;
If I comment out the "WHERE range_start ... line the query runs fine, but pulls all data
However, if I run the complete query, it does not error, but there are no results (I have 4 entries in column cal_created in the last 3 weeks).
If some one can help I'd really appreciate it
Try using INTERVAL and either NOW() or CURDATE()..
WHERE FROM_UNIXTIME (CAL_CREATED,'%Y-%m-%d') BETWEEN CURDATE() - INTERVAL 30 DAY AND CURDATE()
curdate is just the date portion of the day
if you want to include the time use NOW()
WHERE FROM_UNIXTIME (CAL_CREATED,'%Y-%m-%d') BETWEEN NOW() - INTERVAL 30 DAY AND NOW()
you could also make a new date to use the between with
WHERE FROM_UNIXTIME (CAL_CREATED,'%Y-%m-%d') BETWEEN DATE_SUB(NOW(), INTERVAL 30 DAY) AND NOW()
SOURCE: http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html
NOTE: the dates need to be formatted correctly in order for it to work
FULL QUERY:
SELECT
CAL_OWNER,
CAL_TITLE,
FROM_UNIXTIME (CAL_CREATED, '%m-%d-%y') AS CREATED_AT,
FROM_UNIXTIME (RANGE_START, '%Y-%m-%d') AS DATE2BESEEN,
CASE CAL_REFERRAL_TYPE
WHEN 1 THEN 'NoReferral'
WHEN 2 THEN 'CareyGuide'
WHEN 3 THEN 'Education'
WHEN 4 THEN 'Employment'
WHEN 5 THEN 'Housing'
WHEN 6 THEN 'Medical'
ELSE 'NA'
END AS REFERRALS
FROM EGW_CAL
WHERE FROM_UNIXTIME(CAL_CREATED,'%Y-%m-%d') BETWEEN (NOW() - INTERVAL 30 DAY) AND NOW()
ORDER BY REFERRALS ASC;
CAL_CREATED is a UNIX timestamp,
NOW() will return a MySQL timestamp.
They don't mix automatically. So use
WHERE CAL_CREATED
BETWEEN UNIX_TIMESTAMP(NOW() - INTERVAL 30 DAY) AND UNIX_TIMESTAMP(NOW());
Note:
I wouldn't recommend to go the other way
WHERE FROM_UNIXTIME(CAL_CREATED) BETWEEN ...
because MySQL can't use an index in this case.
The correct where clause uses internval:
WHERE CAL_CREATED BETWEEN NOW() - interval 30 day AND NOW()
The use of single quotes is reminiscent of Postgres. In MySQL, it ends up treating the value of now() as an integer. And it subtracts the string value "30 days" from it.
When now() is treated as an integer, it also has hours, minutes, and seconds. So you are really subtracting something like 30 seconds. Here is the documentation on now().

date condition to retrieve data in a 24 hour time window mysql

I am using perl and DBI to query a mysql table. I need to retrieve all rows (aprox. 75,000 rows from 3 separate databases) within the past 24 hours, ideally between 12:00 am and 11:59 pm or 00:00:00 and 23:59:59.
I was using a WHERE date condition like this:
SELECT *
FROM table_name
WHERE insert_date >= DATE_SUB(NOW(), INTERVAL 1 DAY);
Then I would run my script at midnight using cron. This worked well enough, but due to a regular large volume of traffic at midnight and the size of the queries, the execution time scheduled with cron is now 3:00 am. I changed my sql to try and get the same 24 hour period from an offset like this:
SELECT *
FROM table_name
WHERE insert_date
BETWEEN DATE_SUB(DATE_SUB(NOW(), INTERVAL 3 HOUR), INTERVAL 1 DAY)
AND DATE_SUB(NOW(), INTERVAL 3 HOUR);
This seems to work fine for my purposes but I want to ask, is there is a more readable and more accurate way, using mysql, to get all rows from the past 24 hours ( between 00:00:00 and 23:59:59 time window ) once a day while running the query from an offset time? I am generally new to all of this so any critiques on my overall approach are more than welcome.
I presume insert_date is a DATETIME?
It seems pointless to go to all the trouble of building two limits and using BETWEEN. I would simply check that DATE(insert_date) is yesterday's date. So
WHERE DATE(insert_date) = CURDATE() - INTERVAL 1 DAY
BETWEEN DATE_FORMAT(DATE_SUB(NOW(), INTERVAL 1 DAY), "%Y-%M-%d 00:00:00")
AND DATE_FORMAT(DATE_SUB(NOW(), INTERVAL 1 DAY), "%Y-%M-%d 23:59:59")
You could also use Perl date formatting functions to produce the same date-time strings, and interpolate them into the query.
....
WHERE insert_date BETWEEN CURRENT_DATE - INTERVAL 1 DAY
AND
CURRENT_DATE - INTERVAL 1 SECOND;
The lower bound will be coerced to yesterday's YYYY-MM-DD 00:00:00, and this WHERE predicate will be able to make use of an index on insert_date.
Considering that DATE(NOW()) implicitly means midnight this morning, the obvious solution is to take that value and subtract a day for the start... and subtract a second for the end.
BETWEEN DATE_SUB(DATE(NOW()), INTERVAL 1 DAY)
AND DATE_SUB(DATE(NOW()), INTERVAL 1 SECOND)

Get rows from the beginning of the current hour

I have data stored in a mySQL database and I want to retrieve the rows that have been inserted between now and the beginning of the current hour: not in the last hour, since the start of the hour. For example, at 9:16 I want the rows from 9:00 until now. My time is stored in datatime format. How can I do this?
The query below selects all rows that are between the current hour and current time. So if current time is 10:11 it will select all rows between 10:00 and 10:11 within current date.
SELECT *
FROM Test
WHERE mytime BETWEEN DATE_FORMAT(NOW(), '%Y-%m-%d %H:00:00')
AND DATE_FORMAT(NOW(), '%Y-%m-%d %H:%i:%s')
I Tested it and worked.
The first thing that comes to mind is:
select *
from t
where date(now() ) = date(t.timeval) and
hour(now() ) = hour(t.timeval)
I made the assumption that you don't have future records in the data. If so, then this comes to mind:
select *
from t
where date(now() ) = date(t.timeval) and
hour(now() ) = hour(t.timeval) and
timeval < now()
SELECT * FROM times WHERE t >= DATE_FORMAT(NOW(),'%Y-%m-%d %H:00:00');