I have the query that gets the data for the previous month. However, the time stored in the tables are in UTC (+00:00).
How do I adjust the query to convert the TimeZone from UTC to Australia/Sydney (+10:00) in the query?
My query is:
SELECT
DATE_FORMAT(pr.start_date,'%b %y') as start,
p.name as publisher,
sum(ifnull(pr.revenue,0)) as revenue
FROM trk.publisher_revenue pr
left outer join publisher p on p.pubid = pr.pubid
where pr.start_date between DATE_SUB(DATE_FORMAT(NOW() ,'%Y-%m-01'),
INTERVAL 1 MONTH) AND DATE_FORMAT(NOW() ,'%Y-%m-01')
and pr.status = 1
group by DATE_FORMAT(pr.start_date,'%b %y'),pr.pubid
order by pr.start_date, p.pubid;
The column where the date are stored is the start_date. It has the datatype DATETIME and the format it is stored is in e.g. 2014-09-01 00:00:00
The only solution for my problem is to adjust the query.
Desired result: Display the data, after converting the TimeZone to 'Australia/Syndey' or GMT +10:00.
It looks like you're trying to get your month boundaries right for a reporting query.
Your start_date values are stored in DATETIME data items, so you can convert them to UTC+10:00 like this:
pr.start_date + INTERVAL 10 HOUR
Simply rewrite your code to do that. For example, you can get the month in which each row falls with this expression:
DATE_FORMAT(pr.start_date + INTERVAL 10 HOUR,'%b %y')
But, there's a complication. The value of the expression NOW() depends on your MySQL connection's current time zone setting. This makes a difference for your range selection. If your time zone setting is UTC, you should apply the same ten-hour offset to NOW() and related values. For example, rewriting the query in your question, you'd get this:
where pr.start_date + INTERVAL 10 HOUR
between
DATE_FORMAT(NOW() + INTERVAL 10 HOUR ,'%Y-%m-01') - INTERVAL 1 MONTH
and /* WRONG ! */
DATE_FORMAT(NOW() + INTERVAL 10 HOUR ,'%Y-%m-01')
But, you really can't use BETWEEN for date ranges because it includes the end-date of the range, and you want to exclude that. So your date-selection query should be
where pr.start_date + INTERVAL 10 HOUR >=
DATE_FORMAT(NOW() + INTERVAL 10 HOUR ,'%Y-%m-01') - INTERVAL 1 MONTH
and pr.start_date + INTERVAL 10 HOUR <
DATE_FORMAT(NOW() + INTERVAL 10 HOUR ,'%Y-%m-01')
Notice the use of >= for the start of the date range and < for the end.
You probably should refactor that expression so it can exploit a range scan.
where pr.start_date >=
(DATE_FORMAT(NOW() + INTERVAL 10 HOUR ,'%Y-%m-01') - INTERVAL 1 MONTH)
- INTERVAL 10 HOUR
and pr.start_date <
(DATE_FORMAT(NOW() + INTERVAL 10 HOUR ,'%Y-%m-01'))
- INTERVAL 10 HOUR
It looks like there's a lot of redundant adding and subtracting of ten hours going on, but it's necessary because of the date-truncation in the middle of things.
Finally, I believe Sydney's daylight savings time zone, presently in effect, is UTC +11:00 not UTC + 10:00. You might want to double check your offset.
Related
Is there a way to create a dynamic way to make a sql query that queries the data between the first day and the last day of every current month? I have a field called created and and I want to get the data where created is between the first day and the last day of the month.
Something like this will do it:
WHERE created BETWEEN DATE_FORMAT(Now(), '%Y-%M-01 00:00:00') AND DATE_FORMAT(DATE_ADD(DATE_ADD(Now(),INTERVAL 1 MONTH), INTERVAL -12 HOUR), '%Y-%M-%d 23:59:59')
It’s not pretty, but it gets the job done.
Is there a way to create a dynamic way to make a sql query that queries the data between the first day and the last day of every current month?
A simple method uses just date functions:
where created_at >= curdate() + interval (1 - day(curdate()) day and
created_at < (curdate() + interval (1 - day(curdate()) day) + interval 1 month
In addition to only using date arithmetic, this doesn't have an error of missing the last second of the month.
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.
Why this query is not working
SELECT * FROM history WHERE DATE(date) < CURDATE() + 30
I am trying to get the data from 30 days but my query is not working.Why
What does +30 mean? Days? Years? Months? Hours? You need to use (the proper syntax) a format MySQL understands:
SELECT * FROM history WHERE DATE(date) < CURDATE() + INTERVAL 30 DAY
To get the data from today on to 30 days after current day, you've got to set an upper and an lower limit, so use:
SELECT * FROM history WHERE
date >= CURDATE()
AND
date < CURDATE() + INTERVAL 31 DAY
Please note that by not using a function on your date column you won't prohibit MySQL to use an index on this column.
The lower limit should be obvious, the upper limit means that you've got the complete day that's 30 days later than today. If you use + INTERVAL 30 DAY instead this last day is excluded from the result.
Because you're not using the right construct, try:
SELECT * FROM history WHERE DATE_ADD(date, INTERVAL 30 DAY);
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)
I need to get the result from the table, which the date should be difference of 5 from the current date.
ie., specific_date column is present in my table. The format of the date is YYYY-MM-DD.
I need the query something like,
SELECT * FROM `table_name` WHERE DATEDIFF(NOW(), specific_date) < 5
It looks like you are trying to do this:
WHERE specific_date < (NOW() + 5 days)
First of all, think carefully about the boundary cases. These boundary cases can bite your ankles in SQL. Do you actually want
WHERE specific_date <= (NOW() + 5 days)
Do your specific_date columns timestamps contain only days (that is dates with times equal to midnight) or do they contain dates and times? If you're going to get the results you want, you need to be careful about those details.
At any rate, try this:
WHERE specific_date <= DATE_ADD(NOW(), INTERVAL 5 DAY)
This is a good way to do such a lookup. The column value stands alone on one side of the inequality predicate (the <=) so mySQL can do an index range scan if you have an index on the column.
Date arithmetic in MySQL is quite flexible. You can do
NOW() + INTERVAL 10 SECOND
NOW() - INTERVAL 2 MINUTE
NOW() + INTERVAL 4 HOUR
CURDATE() - INTERVAL 1 WEEK /* midnight one week ago */
LAST_DAY(NOW()) + INTERVAL 1 DAY - INTERVAL 1 MONTH /*first day of present month*/
NOW() - INTERVAL 1 QUARTER
CURDATE() - INTERVAL 5 YEAR
and so forth.
Have a look at the BETWEEN operator.
expr BETWEEN min AND max
Another way is to use the DATE_SUB operator
WHERE date_column > DATE_SUB(NOW(), INTERVAL 5 DAY)