MySQL Select rows <= now(), using separated time fields - mysql

I have a table 't' with date(yyyy-mm-dd), hour(1-12), minute(00-59), ampm(a/p), and timezone(pst/est) fields.
How can I select the rows that are <= now()? (ie. already happened)
Thank you for your suggestions!
edit: this does it without attention to the hour/minute/ap/tz fields:
SELECT * FROM t.date WHERE date <= now()

Here's one way to do it - combine all your seconds, minutes, etc into a date and compare to NOW(), making sure you do the comparison in the same time-zone. (Untested):
SELECT *
FROM t
LEFT JOIN y ON t.constant=y.constant
WHERE CONVERT_TZ(STR_TO_DATE(CONCAT(date,' ',hour,':',minute,' 'ampm),
'%Y-%m-%d %l:%i %p' ),
timezone,"SYSTEM") < NOW();
If your hour is 01 - 12 not 1-12 then use %h instead of %l in the STR_TO_DATE.
The STR_TO_DATE tries to stick your date and time columns together and convert them into a date.
The CONVERT_TZ(...,timezone,"SYSTEM") converts this date from whatever timezone is specified in the timezone column to system time.
This is then compared to NOW(), which is always in system time.
As an aside, perhaps you should make a single column date using MySQL's date datatype, as it's a lot easier to do arithmetic on that!
For reference, here is a summary of very useful mysql date functions where you can read up on those featuring in this answer.
Good luck!

SELECT * FROM t
WHERE `date`<=DATE_SUB(curdate(), INTERVAL 1 DAY)
OR (
`date`<=DATE_ADD(curdate(), INTERVAL 1 DAY)
AND
CONVERT_TZ(CAST(CONCAT(`date`,' ',IF(`hour`=12 AND ampm='a',0,if(ampm='a',`hour`,`hour`+12)),':',`minute`,':00') AS DATETIME),'GMT',`timezone`)<=NOW()
)
Rationale for date<=DATE_[ADD|SUB](curdate(), INTERVAL 1 DAY):
The fancy conversion is quite an expensive operation, so we don't want it to run on the complete table. This is why we pre-select against an UNCHANGED date field (possibly using an index). In no timezone can an event being more than a day in current timezone's past be in the future, and in no timezone can an event more than a day in the curent timezone's future be in the past.

Related

mySQL need timezone displayed in EST but cannot change global time settings

I am facing an odd issue. The global timezone setting in the mySQL is in UTC. There are multiple tables (sub databases) within a single instance of mySQL (I am using database.NET as the manager); so I can't change the global timezone.
The following is my query. All I need is to display the times in EST. I have seen a few solutions with ##sessionTimeZone but they didn't work. Furthermore, I am running into the issue of data being collected till 9PM EST, but in UTC that's 1AM on the next day.
My main confusion is that the data is already stored in the table in UTC; however, I would like to see those datetime fields displayed in EST.
cartId is a numerical field
createDate is a datetime field
I am just using http://fishcodelib.com/database.htm and connecting to the mySQL database.
SELECT DATE(createDate)
,DATE_FORMAT(createDate, '%l%p') as HourOfDay
,count(cartId) as numCarts_ALL
FROM carts
WHERE createDate >= '2014-09-24'
AND createDate < '2014-10-01'
AND HOUR(createDate) >= 10 AND HOUR(createDate) <21
GROUP by DATE(createDate),HOUR(createDate)
;
I will really appreciate any help.
Thanks again
DATETIME data types are not affected by either the global or connection-local timezone setting. That only works for TIMESTAMP data types. That explains why your attempts to muck around with ##session.TimeZone don't have any effect. NOW() and CURDATE() are affected, though.
You say your DATETIME data is stored in UTC. That's excellent. Life is much easier when you store your data that way.
Before you use the advice I'm about to give you, please make sure your MySQL server has its timezone tables loaded correctly. Do this command and make sure you don't get a NULL result.
SELECT CONVERT_TZ(NOW(), 'America/New_York', 'UTC')
If this doesn't work -- if you get a NULL or an error -- you need to get your server people to load the time zone tables. They should do this. They know how. (If they don't, you should get a new service provider.)
We need to use the timezone named 'America/New_York' because presumably you want to switch automatically between EDT and EST on the appropriate dates each year.
Now, to retrieve a correctly converted UTC DATETIME value from a table, you do this:
SELECT CONVERT_TZ(createDate, 'UTC', 'America/New_York') AS createDate
FROM yourTable
This is cool because you can make the timezone setting a user preference if you have users in various time zones.
To store a local time value as UTC just do it in reverse. For example.
INSERT INTO yourTable (createDate) VALUES (CONVERT_TZ(?, 'America/New_York', 'UTC'))
Now do
SELECT NOW()
and look to see whether NOW() is in local time or in UTC. If it is in UTC, then you should start your session by doing SET TIME_ZONE='America/New_York' . This will get your timezone set right so NOW() and CURDATE() do what you want.
Then, to fetch yesterday's (local time) rows from your table do this:
WHERE createDate >= CONVERT_TZ(CURDATE(),'America/New_York','UTC') - INTERVAL 1 DAY
AND createDate < CONVERT_TZ(CURDATE(),'America/New_York','UTC')
This will convert midnight local time to UTC and fetch the range of items.
To get all the data from 4pm today until 2am tomorrow, you could do this:
4pm today (localtime) is CURDATE() + INTERVAL 16 HOUR . 2am tomorrow is CURDATE() + INTERVAL 26 HOUR or you could write it CURDATE() + INTERVAL 1 DAY + INTERVAL 2 HOUR
So fetching that range would need this:
WHERE createDate >= CONVERT_TZ(CURDATE() + INTERVAL 16 HOUR,'America/New_York','UTC')
AND createDate < CONVERT_TZ(CURDATE() + INTERVAL 26 HOUR,'America/New_York','UTC')
You could also use ADDTIME(CURDATE(),'16:00') to obtain a DATETIME value for 4pm today, if you'd rather combine ordinary date objects and ordinary time objects. Similarly, you could get 2am tomorrow like this:
ADDTIME(CURDATE(),'02:00') + INTERVAL 1 DAY
Notice that this form of WHERE clause allows a range scan on an index on the createDate column. This is very good for performance.
To convert datetime from a timezone to another timezone:
SELECT DATE(CONVERT_TZ(createDate, '+00:00', '-04:00')) createdDate,
HOUR(CONVERT_TZ(createDate, '+00:00', '-04:00')) hourOfDay
FROM carts
WHERE
createDate BETWEEN CURRENT_DATE + INTERVAL 16 HOUR AND CURRENT_DATE + INTERVAL 26 HOUR
GROUP BY createdDate, hourOfDay
For different dates
SELECT DATE(CONVERT_TZ(createDate, '+00:00', '-04:00')) createdDate,
HOUR(CONVERT_TZ(createDate, '+00:00', '-04:00')) hourOfDay
FROM carts
WHERE
createDate BETWEEN '2014-09-24' AND '2014-10-01'
GROUP BY createdDate, hourOfDay
HAVING hourOfDay <= 2 OR hourOfDay >= 20
You might think "why not in WHERE clause?"
If functions are used in the where clause, indexes can't be used (which results in a full table scan). So just filter out the records within the daterange, and the hours in the HAVING clause
FYI
CURRENT_DATE + INTERVAL 16 HOUR
Is short for
DATE_ADD(CURRENT_DATE, INTERVAL 16 HOUR)

mySQL gather data between hours of the day passing through midnight with date increment

I am trying to get data from a database between 8PM (say, today) and 2AM tomorrow.
I have been using clauses such as where hour(date_field)>=20 and hour(date_field) <23 to obtain data in the same day.
Here the date_field is datetime
All I want is to be able to tell SQL to get data after 8PM today, increment the datefield and then get data till 2AM tomorrow.
Any help will be appreciated.
The normal pattern for retrieving rows based on a datetime range is perform comparisons on the bare column, comparing the column value to constants derived from expressions.
To get rows for a single contiguous range, 8PM today to 2AM tomorrow, for example:
WHERE t.date_column >= DATE(NOW()) + INTERVAL 20 HOUR
AND t.date_column < DATE(NOW()) + INTERVAL 26 HOUR
To unpack that a little bit: NOW() returns current datetime, the DATE() function truncates the time portion to midnight, then we add back in enough hours to get '8PM today', or enough hours to get '2AM tomorrow'.
If you are meaning to retrieve multiple "8PM to 2AM" periods, for a whole series of days.
First, you'd want an upper and lower bound of the date_column to be retrieved (unless you want every possible date)
WHERE t.date_column >= '2014-08-01 20:00:00'
AND t.date_column < '2014-10-02 02:00:00'
From that, we need to filter out all of the rows that aren't between 8PM and 2AM. One convenient way to do that would be to "subtract" two hours from the datetime col, and check for hour >= 6PM.
AND HOUR(t.date_column + INTERVAL -2 HOUR) >= 18
Note that the expression involving date_column will need to be evaluated for EVERY row in the table, unless there are some other predicates that filter rows out. With a suitable index available, MySQL can use an index range scan operation for predicates of the form date_column >= const and date_column < const. (It can't do that when the column is wrapped in a function or expression.)

Select query for two weeks ago

In my database table I have a field for date (varchar field to save date in yy-mm-dd format ), now I want to select records for two weeks ago.
How can i do it ?
Implicit date arithmetic is fairly flexible in MySQL. You can compare dates as strings without explicit use of CAST() or DATE(), but you're trusting MySQL to interpret your format correctly. Luckily for you, it will do just fine with yy-mm-dd.
I would recommend using BETWEEN and INTERVAL so that your query is easily readable; for example:
SELECT * FROM Holidays
WHERE Date BETWEEN (NOW() - INTERVAL 14 DAY) AND NOW();
The only trick with BETWEEN is that you have to put the lower bound first and the upper bound second; for example, if you write BETWEEN 5 AND 2, this always evaluates to FALSE because there is no value that can be greater than or equal to 5 while also being less than or equal to 2.
Here's a demo of the query in action at SQL Fiddle, and a list of the recognized INTERVAL expressions in MySQL.
Note that the parentheses around the expression NOW() - INTERVAL 14 DAY are not required but I would recommend using them here purely for the sake of clarity. It makes the predicate clause just a little bit easier to read in the absence of proper syntax highlighting, at the expense of two characters.
Ideally you should be using date types to store dates, but being that's not the case, you should look into casting to date then comparing.
select * from yourtable where cast (yourdate as Date) BETWEEN Date_Add(CURDATE(), INTERVAL -21 Day) and Date_Add(CURDATE(), INTERVAL -14 Day)
Note, this is untested and may need a little tweaking, but should give you a general idea of what you need to do.
Also, if it's possible, you should really look into converting the varchar field to a date field....they have date types to prevent this sort of thing from happening, although i know changing field types isn't always a possibility.
you can simply do with ADDDATE to get 14 days ago. compare string with date will work.
SELECT *
FROM your_table
WHERE your_date >= ADDDATE(NOW(), -14) AND your_date < NOW()
I use this for select data in past of past
SELECT * FROM Holidays
WHERE a.dDate >= DATE( NOW( ) ) - INTERVAL 14
DAY AND a.dDate <= DATE( NOW( ) ) - INTERVAL 8

Getting the date from DATETIME. mySQL

I wish to query for
MyDate= '2013-07-08'
From the following records
MyDate
2013-07-08 09:15:21
2013-07-08 09:15:48
2013-07-09 09:20:39
I have come up with some ugly stuff :
MyDate > '2013-07-07 23:59:59' AND MyDate < '2013-07-09 00:00:01'
Is there a better/simple/elegant way to do this?
Use DATE() to isolate the date portion of the datetime expression.
WHERE DATE(MyDate) = '2013-07-08'
If your trying to compare dates use this. If not disregard.
This may not be the most perfect way but, i have used this in the past. Basically i would format both dates so they can be used with a greater than or equal to statement(YEAR/MONTH/DAY).
SELECT * FROM table
WHERE MyDate > DATE_FORMAT(2013-07-07 23:59:59, '%Y%m%y')
AND MyDate < DATE_FORMAT(2013-07-09 00:00:01, '%Y%m%y')
The normative pattern to matching the date portion of a DATETIME in a predicate (e.g. a WHERE clause) is:
WHERE MyDate >= '2013-07-08'
AND MyDate < '2013-07-08' + INTERVAL 1 DAY
When no time component is supplied, MySQL uses midnight as the time component, so there's no need to supply a time component of midnight. The bare column references in the predicate allow for MySQL to consider making efficient range scan on an index on the MyDate column.
For completeness, we'll note that it's also possible to use a ETWEEN operator. But because the "high side" comparison with the BETWEEN is a "less than or equal to", to get just values with date component of a single day, we'd need to back up the smallest fraction of time, which for a DATETIME is a single second:
WHERE MyDate BETWEEN '2013-07-08'
AND '2013-07-08' + INTERVAL 1 DAY + INTERVAL -1 SECOND
(If we had a datatype that had a finer resolution, we'd want to step back from the next day by that smallest unit of resolution.)
To avoid that issue, how fine a resolution is on a given datetime/timestamp datatype (more of an issue with other databases such as SQL Server than MySQL), I just have a preference of the former pattern, using a predicate like:
dateexpr >= midnight and dateexpr < midnight of next day
That's unambiguous, and there's no possible way to have a time value of 23:59:59.997 to be missed, and no possibility of getting exactly at midnight of the next day included.
Because the default time component, when none is supplied, is midnight, the first query predicate is equivalent to:
WHERE MyDate >= '2013-07-08 00:00:00'
AND MyDate < '2013-07-08 00:00:00' + INTERVAL 1 DAY
I think all those extra zeros to explicitly specify a time value of midnight are unnecessary clutter.

How best to store time ranges like Tuesday 10-11am?

It's not a one-of, not only one particular Tuesday, so I won't use time stamps.
I mean something like "every Tuesday from 10am to 11am". What's the best way to store this to make it easy to check from my (Delphi) application if the current time is between those permitted times?
Sounds like the time range would simply be an attribute of whichever primary object you are working with, as such you could add a separate table to store the time range data, something like:
object_time_range
id
object_id
day
hour_start
hour_end
frequency_id
-- Update --
In hindsight I would probably abstract hour from the column names in favor of time, just in case you need to consider alternative time frames. And day should be day_id, even though its a static list, I always like going with ints in this situation.
object_time_range
id
object_id
day_id
time_start
time_end
frequency_id
Store the variable as datetime or timestamp,
you can use mysql date functions such as
date_format and date_add
to get the rows which fall within required dates and time
if you store it as text it would be very difficult.
E.g To get rows for the past one day
select * from tableName
where dateField > date_add(now(),interval -1 day) and
dateField < now()
rows for the past one Week
select * from tableName
where dateField > date_add(now(),interval -1 week) and
dateField < now()
Check date_add funciton