I can provide with a general explanation, but it may not apply specifically to your particular case:
The way decision making works is by evaluation cost of execution plan, then picking up what is hopefully the cheapest plan. This you already know.
When it comes to indexing, though, stuff are getting interesting. The way to evaluate the usefulness or viability of an index is to estimate the selectivity given some value.
For the moment, forget about your FULLTEXT index, and let's assume a simple index on some column col1
, and another index on some column col2
. Given the following two queries:
SELECT * FROM t WHERE col1 < 10 and col2 = 4;
SELECT * FROM t WHERE col1 BETWEEN 100 AND 110 and col2 = 4;
It may happen that the query is evaluated differently in these two cases. Why? Because it may happen that col2 = 4
returns more rows than col1 < 10
, in which case we prefer to use index on col1
. But then, it may return less rows than col1 BETWEEN 100 AND 110
, in which case we prefer the index on col2
.
Your case is not very much different. MySQL estimates the number of rows returned by some index query. When you use more columns, MySQL gets the impression your index is likely to result with few rows. So it chooses to start with TableA
, then joins what should be very few rows with TableB
.
But if MySQL believes the index to return many rows, it may prefer starting with TableB
. Why is that? Because you are sorting on indexed columns of TableB
. Sorting is a lot of work, too. So MySQL may choose to first sort the rows, then join to TableA
and filter by fulltext index. It may not be a bad idea if the fulltext search yields with many rows anyhow.
OPTIMIZE rebuilds the table. This (for InnoDB) squeezes out some of the fragmentation and wasted space. This is unlikely to make a noticeable difference in any query.
Also, OPTIMIZE does an ANALYZE. This has a chance of changing the statistics, thereby leading to a different (better or worse) EXPLAIN plan.
Since ANALYZE is much faster (on InnoDB) than OPTIMIZE, just do the ANALYZE.
Various non-ANALYZE actions cause an ANALYZE to be done.
ANALYZE randomly probes the BTrees, gathering stats. Sometimes the resulting stats are poor. There is effectively no way to prevent this from happening. Several partial hacks have been created over the years; 5.6.7 gets close to eliminating this problem with ANALYZE. Here's one of them: http://dev.mysql.com/doc/refman/5.6/en/innodb-parameters.html#sysvar_innodb_stats_persistent_sample_pages
Best Answer
First, what you need to focus on are the three fields in the query
The index you have
(created_at,person_id)
will make the query do an index scan across all the days ofcreated_at
afterCURRENT_DATE
looking for theperson_id
.SUGGESTION #1 : You will definitely need a different index
MyISAM
If
login_events
is MyISAM, this is the index you needThis changes the query because the query will look for the specific
person_id
and scan all days forperson_id
1 only. The reasonid
is included in the index ? The query will retrieve theid
from the index only file rather than the table. That way, all 3 fields are retrieved from the index file instead of 2 fields from the index and 1 from the table.InnoDB
If
login_events
is InnoDB, this is the index you needThe reason I recommend this is the same, but you do not need to include
id
. Why? All index pages include an index point back to the clustered index so retrieval of an index will intrinsically access the row anyway, thus accessing id. Addingid
to the index would simply be redundant.SUGGESTION #2 : Change the Date Comparison
From the expression
I can tell that
created_at
is eitherDATETIME
orTIMESTAMP
.The expression forces the query to convert every row's DATETIME value of
created_on
into a DATE.Therefore, instead of
express the date comparison as a time comparison starting from midnight of today
CAVEAT
Since the table is so small, either storage engine would be fine. I would give the edge to MyISAM.
Give it a Try !!!