Select rows where datetime = date and still use index - mysql

I have a table with a datetime column which is indexed. I'd like to select all the rows for a specific date. Previously I've been using this:
SELECT *
FROM `table`
WHERE DATE(date_column) = '2016-04-12';
This works fine, however I've been noticing performance issues due to the fact that MySQL does not make use of the index once I apply the DATE() function. I've also tried:
SELECT *
FROM `table`
WHERE SUBSTR(date_column,1,10) = '2016-04-12';
to no avail. So far the only thing that seems to work is:
SELECT *
FROM `table`
WHERE date_column >= '2016-04-12'
AND date_column < '2016-04-13'
This last query successfully uses the index but I'd like to know if there is a way to do this with only a single condition.

I like this code pattern; it is clear, precise (no confusion over midnight), and can use the index:
WHERE date_column >= '2016-04-12'
AND date_column < '2016-04-12' + INTERVAL 1 DAY
It even works 'correctly' for DATE, DATETIME, TIMESTAMP, DATETIME(6), etc.
'2016-04-12 00:00:00.000' is identical to '2016-04-12' when used in this context. I prefer the latter because it is more concise.

Related

Slow sql statement when using variables

I have the following SQL statement running against a MariaDB 10.1.26 with ~2.000 rows with instant results.
select value, datetime from Schuppen
where (value = (select min(value) from Schuppen where (measure = 'temp')
and datetime between '2018-11-01 00:00:00' and '2018-11-02 00:00:00'))
and datetime between '2018-11-01 00:00:00' and '2018-11-02 00:00:00';
When I use the following statement with variables for the datetime fields, the execution takes ~5.5 seconds.
set #startdate = cast('2018-11-01 00:00:00' as datetime);
set #enddate = cast('2018-11-02 00:00:00' as datetime);
select value, datetime from Schuppen
where (value = (select min(value) from Schuppen where (measure = 'temp')
and datetime between #startdate and #enddate))
and datetime between #startdate and #enddate;
The more data rows I have, the longer it takes to execute the statement. Seems like the variables change the behaviour of the statement somehow.
What's wrong here?
I use MySQL Workbench and #variables are very useful to query/search different tables for a given attribute. I ran into a similar issue. After scouring through different threads and trying different things, it worked well when I set the #variable to be of exactly the same type and same encoding as the column in the table(s) that I am searching for that variable.
For example:
SET #keyword = CONVERT(CAST("KEYWORD" AS CHAR(8)) USING ASCII);
In this case, the search column cname in my table customer is of type CHAR(8) and encoded using ASCII:
SELECT * FROM CUSTOMERS WHERE cname=#keyword;
If you have multiple tables to query, where cname is CHAR(10) in one and CHAR(8) in another, then you can do the following:
SET #keyword = "KEYWORD";
SELECT * FROM CUSTOMERS WHERE cname=CONVERT(CAST(#keyword AS CHAR(8)) USING ASCII);
SELECT * FROM EMPLOYEES WHERE cname=CONVERT(CAST(#keyword AS CHAR(10)) USING ASCII);
The problem is that the query optimizer does a bad job on finding a suitable index when using variables. This is a known issue.
If you use EXPLAIN on both queries, you will see the difference. Just try to avoid variables when not necessary.
For the first query, the optimizer "sees" the chosen values and decides an index can be perfectly used to satisfy the selected range more efficiently.
For the second query, the optimizer is unaware of the two values that define the range, and decides to fall back to a FULL SCAN instead.

mysql use wildcard in request involving timestamp

im trying to select all the rows having a particular year in mysql.
the datetime is in a format such as 2016-12-02 10:00:00 so i am trying to use wildcard to do it. However i tried
SELECT * FROM all-data.prices where YEAR(timestamp)= "2005";
and SELECT * FROM all-data.prices where timestamp= "2005%" ;
and it didnt work for me.
Any suggestion on which is the correct syntax for such select statement?
It is not the best idea to use a FUNCTION on a field in the WHERE Clause. Then MySQL must calculate YEAR(timestamp) from every ROW before it can compare. So it is a FULL TABLE SCAN and cant use a INDEX.
A better was is to compare the timestamp with a Range
SELECT * from your_table WHERE `timestamp` >= '2015-01-01 00:00:00' AND `timestamp` < '2016-01-01 00:00:00';
Then MySQL can use a INDEX (if there is one)
To use % you also must use the LIKE Keyword
SELECT '2015-01-01 23:50:00' LIKE '2015%';
in your sample
SELECT * from your_table WHERE `timestamp` LIKE '2015%';

Is it possible to optimize mysql query with datatime field difference?

I have a table with a DATETIME field called date_created, and need to check for some data with this kind of query:
SELECT * FROM table WHERE UNIX_TIMESTAMP(date_created) + $number < UNIX_TIMESTAMP()
Is it possible to do this without the UNIX_TIMESTAMP() function? Maybe with NOW())?
And if it is, will it be faster?
Is it possible to do this without the UNIX_TIMESTAMP() function? Maybe with NOW())?
Not exactly. Whilst you could do the following, it won't achieve the exact same results:
SELECT * FROM `table` WHERE date_created + INTERVAL $number SECOND < NOW()
The reason is that, unlike TIMESTAMP, DATETIME is neither intended nor capable of representing a specific instance in time; rather, it effectively represents the display of a calendar/clock (not the same thing).
When using the above query, which does not go via a UTC timestamp, the comparison is merely a question of whether a clock right now would show a date/time later than that which was recorded in the database (plus $number seconds).
However, when converting to a UTC timestamp, the timezone of the respective clock displays become relevant and because many timezones are not constant (e.g. they often move around for daylight savings), multiple DATETIME values could give rise to the same UTC timestamp.
For example:
CREATE TABLE `table` (date_created DATETIME);
INSERT INTO `table` VALUES ('2012-10-28 01:00:00'), ('2012-10-28 02:00:00');
Then compare the results of the two queries when run at 2012-10-28 02:00:00 in the UK:
Your original query:
SET SESSION time_zone = 'Europe/London';
SELECT *
FROM `table`
WHERE UNIX_TIMESTAMP(date_created) + 100
< UNIX_TIMESTAMP('2012-10-28 02:00:00');
The alternative query above:
SELECT *
FROM `table`
WHERE date_created + INTERVAL 100 SECOND
< '2012-10-28 02:00:00';
And if it is, will it be faster?
Probably (a lexicographic order for the comparison will suffice, versus parsing & converting the DATETIME values to UTC according the session timezone's rules followed by subtraction and sign inspection), but I'd advise performing your own benchmarks.
Using eval expression in WHERE clause should be avoided if possible. It prevents correct utilization of indexes. If possible do the math in code and send the values as query parameters.

select all records that have a certain date: easier way?

I'm selecting records that have a certain date in a datetime column, but it feels a bit sloppy, and I'm wondering if there's a better way. My query looks like this:
SELECT *
FROM myTable
WHERE event_datetime
BETWEEN '2012-05-05 00:00:00' AND '2012-05-05 23:59:59'
and it works fine. I'm just wondering if perhaps there's a better way to do this in mySQL.
Better from what perspective?
One would propose to use DATE(event_datetime) = '2012-05-05' but it is terribly inefficient.
So continue using your solution and make sure event_datetime column is covered by index
Use the DATE function on the column in your where clause.
For example:
SELECT *
FROM myTable
WHERE DATE(event_datetime) = '2012-05-05'
You could do WHERE date(event_datetime) = '2012-05-05', but this way can not use index on event_datetime, so if you want to use index of event_datetime, use your original way.

Querying Where date_created without time is same as last_updated without time

In MySQL, I need to write a query (if possible) that finds all rows of a table where the date_created is the same as last_updated. The rub is that I need to ignore the time. Basically, I'm looking for user rows that were created and activated the same day (we don't store an activation date). So presumably the dates would be the same but the times may be different.
You could use the DATE() function, which returns only the date portion of a datetime value. This allows you to compare just the date portion of the values:
SELECT * FROM table_name
WHERE DATE(date_created) = DATE(last_updated)
The timezone may be relevant here. So you may want to cast the datetime values to the user's timezone prior to using the DATE() function, using CONVERT_TZ().
Try this:
SELECT *
FROM table_name
WHERE DATE_FORMAT(date_created, '%Y-%m-%d') = DATE_FORMAT(last_updated, '%Y-%m-%d')
not pretty but works:
SELECT *
FROM table_name
WHERE day(date_created) = day(last_updated) and
month(date_created) = month(last_updated) and
year(date_created) = year(last_updated)