Do MySQL complex indexes involve simple indexes? - mysql

If I set a multi-column index -unique for example- with columns (A, B) and search by A or B independently, will they be as fast as if I also have simple indexes in A and B?
Are those extra simple indexes necessary?

From MYSQL:MySQl 5 Reference
If the table has a multiple-column index, any leftmost prefix of the index can be used by the optimizer to find rows. For example, if you have a three-column index on (col1, col2, col3), you have indexed search capabilities on (col1), (col1, col2), and (col1, col2, col3).
MySQL cannot use an index if the columns do not form a leftmost prefix of the index. Suppose that you have the SELECT statements shown here:
SELECT * FROM tbl_name WHERE col1=val1;
SELECT * FROM tbl_name WHERE col1=val1 AND col2=val2;
SELECT * FROM tbl_name WHERE col2=val2;
SELECT * FROM tbl_name WHERE col2=val2 AND col3=val3;
If an index exists on (col1, col2, col3), only the first two queries use the index. The third and fourth queries do involve indexed columns, but (col2) and (col2, col3) are not leftmost prefixes of (col1, col2, col3).

If you create any index that is (A,B), MySQL can utilize that index for queries and sorts that have just A, or A then B. It can not use it for "B". The basic idea is that any prefix of the index is useful.
You don't have to create a separate one for "A", but you would need one for "B" if B was going to be sorted on or used in a where clause without "A".

Related

Optimize a SQL query with INDEX

I have a very simple query
SELECT col1, col2, col3, col4 FROM table FORCE INDEX (col2)
WHERE col2 IN ('there', 'are, 'around', 'six', 'values', 'here')
with index col2 for col2. My table has around 10 millions row. I used FORCE INDEX here because there are other indices in my table and MySQL uses one of other indices instead of index col2. The other index is very slow for this query.
List of all indices in my table:
INDEX col2 (col2)
UNIQUE INDEX ind1 (col1, col2)
INDEX ind2 (col1, col2)
INDEX ind3 (col2, col1)
This query (with FORCE INDEX) is not slow (takes 6 seconds on AWS RDS free tier) but there is a need to make it as fast as possible. Is there any thing else I could do to speed up this query?
First, you should try not forcing the index on col2, and instead just look at the explain plan. It is likely that a single column index on col2 would be used here. However, you can try adding the following composite covering index on your table:
CREATE INDEX idx ON yourTable (col2, col1, col3, col4);
This index would cover the WHERE clause, and also includes the other columns which appear in the SELECT clause. If it chooses, MySQL could use this index to completely cover the entire query without needing to seek back to the clustered index (i.e. the original table).
INDEX col2 (col2)
UNIQUE INDEX ind1 (col1, col2)
INDEX ind2 (col1, col2)
INDEX ind3 (col2, col1)
Some of these indexes are redundant. MySQL can use (col2, col1) for searches on col2 as well as searches on both col2 and col1. And ind2 is fully redundant with ind1.
The redundancy might be confusing the optimizer.
To cover all combinations of col1 and col2, as well as enforce uniqueness, you only need...
INDEX col2 (col2)
UNIQUE INDEX ind1 (col1, col2)
Removing the redundant indexes will speed up inserts and save space.
See 8.3.6 Multiple-Column Indexes.
The query planner makes its guesses based on table statistics. Sometimes those statistics are out of date. Try running analyze table to update them.

SQL query the last created item performance

I am trying to fetch the last created item in a large table like this:
SELECT `raw_detection`.* FROM `raw_detection`
WHERE `raw_detection`.`duplicated` = 0
AND `raw_detection`.`audio_source_id` = 100
ORDER BY created_at desc LIMIT 1
But this query takes a long time to run (more than 2 seconds).
I have this index:
KEY `index_raw_detections_audio_source`(`audio_source_id`,`duplicated`,`created_at`)
Is there any better way to fetch the last created item for a specific audio source?
Your key references three columns. It cannot be used to speed up queries using only the created_at portion of the key. Try creating an additional key for just created_at.
For reference, from the MySQL doc:
If the table has a multiple-column index, any leftmost prefix of the index can be used by the optimizer to look up rows. For example, if you have a three-column index on (col1, col2, col3), you have indexed search capabilities on
(col1), (col1, col2), and (col1, col2, col3).
MySQL cannot use the index to perform lookups if the columns do not form a leftmost prefix of the index.
https://dev.mysql.com/doc/refman/5.0/en/multiple-column-indexes.html
In your select, specify the columns needed rather than doing select *

Multi-Column Index Behavior in MySQL with Unused Index Column

Suppose I define the following index on a table in a MySQL database:
(col1, col2, col3)
I know that I get indexed search capabilities on (col1), (col1, col2), and (col1, col2, col3).
Do I also get indexed search capabilities on (col1, col3)?
This is my experience with MSSQL so please test with MySQL
Consider a composite index on (col1, col2, col3)
You get an index seek on:
col1
col1 & col2
col1 & col2 & col3
On col2 and col3 you can get an index (not table) scan.
Since the index is smaller than the table this can help search times.
Some times this is a significant impact.
A search on col1 and col3 would (hopefully) be an index seek on col1 and an index scan on col3.
And note if the table is small you will just get some default plans
Need to load up with some data to test
Summing up Marc B's answers from the comments:
You do not get full indexed search capabilities on (col1, col3) from the index (col1, col2, col3); however, you will still get the benefits of a (col1) index with indexed search capabilities from the first level match on the col1 portion of the query. A table scan would then be used on col3.
If it is necessary to have full indexed search capabilities on (col1, col3) (it may not be - see Eugen Rieck's comment), you would need a separate index on (col1, col3).

MySQL Unnecessary Indexes

If I have this index:
(col1, col2, col3)
I know it helps when I search through (col1); (col1, col2); (col1, col2, col3).
If I create another index with the exactly same columns, phpMyAdmin will warn me that one of those indexes may be removed, because they are the same.
However, if I have these indexes:
(col1, col2, col3)
(col1, col2)
(col1)
phpMyAdmin won't warn me at all.
So my question is, are the last two indexes necessary in any case? I think only the first index is enough.
Thank you.
MySQL will only use one index (the leftmost) to optimize the search. Quoting from the documentation:
"If the table has a multiple-column index, any leftmost prefix of the index can be used by the optimizer to find rows. For example, if you have a three-column index on (col1, col2, col3), you have indexed search capabilities on (col1), (col1, col2), and (col1, col2, col3)."
However, if any of the indexes are UNIQUE, then there's probably a good reason for them to be there.
If during your analysis you find that any of the columns are frequently used apart from one another, then you should consider adding separate indexes for each to optimize those queries.
E.g.
ALTER TABLE tablename ADD INDEX (col1), ADD INDEX (col2), ADD INDEX (col3);

MySQL indices and order

This is a question that I've had forever.
As far as I know the order of indices matter. So an index like [first_name, last_name] is not the same as [last_name, first_name], right?
If I only define the first index, does it mean that it will only used for
SELECT * FROM table WHERE first_name="john" AND last_name="doe";
and not for
SELECT * FROM table WHERE last_name="doe" AND first_name="john";
Since I am using a ORM, I have no idea in which order these columns are going to be called. Does that mean that I have to add indices on all permutations? That is doable if I have a 2 column index, but what happens if my index is on 3 or 4 columns?
Index order matters when your query conditions only apply to PART of the index. Consider:
SELECT * FROM table WHERE first_name="john" AND last_name="doe"
SELECT * FROM table WHERE first_name="john"
SELECT * FROM table WHERE last_name="doe"
If your index is (first_name, last_name) queries 1 and 2 will use it, query #3 won't.
If your index is (last_name, first_name) queries 1 and 3 will use it, query #2 won't. Changing the condition order within WHERE clause has no effect in either case.
Details are here
Update:
In case the above is not clear - MySQL can only use an index if the columns in query conditions form a leftmost prefix of the index. Query #2 above can not use (last_name, first_name) index because it's only based on first_name and first_name is NOT the leftmost prefix of the (last_name, first_name) index.
The order of conditions WITHIN the query does not matter; query #1 above will be able to use (last_name, first_name) index just fine because its conditions are first_name and last_name and, taken together, they DO form a leftmost prefix of (last_name, first_name) index.
ChssPly76 is correct that the order of boolean expressions does not have to match the order of columns in the index. Boolean operators are commutative, and the MySQL optimizer is smart enough to know how to match the expression to the index in most cases.
I also want to add that you should learn how to use the EXPLAIN feature of MySQL so you can see for yourself which indexes the optimizer will choose for a given query.
Why not to extend the answer a little bit to make completely everything crystal clear at once.
If the table has a multiple-column index, any leftmost prefix of the index can be used by the optimizer to find rows. For example, if you have a three-column index on (col1, col2, col3), you have indexed search capabilities on (col1), (col1, col2), and (col1, col2, col3).
MySQL cannot use an index if the columns do not form a leftmost prefix of the index. Suppose that you have the SELECT statements shown here:
SELECT * FROM tbl_name WHERE col1=val1;
SELECT * FROM tbl_name WHERE col1=val1 AND col2=val2;
SELECT * FROM tbl_name WHERE col2=val2;
SELECT * FROM tbl_name WHERE col2=val2 AND col3=val3;
If an index exists on (col1, col2, col3), only the first two queries use the index. The third and fourth queries do involve indexed columns, but (col2) and (col2, col3) are not leftmost prefixes of (col1, col2, col3). - MySQL dev