Why is MySQL using this index? Can I force it to use another?

Posted on

Question :

I have a query, and when I call EXPLAIN for it, it appears that my db is using an unhelpful index and ignoring a useful index.

EXPLAIN SELECT id, full_name, agent_id, brand_id FROM `mytable`
WHERE (
    `mytable`.`brand_id` IN (21264)
    OR
    `mytable`.`agent_id` IN (4, 5, 6, 7, 9, 11)
    )
    AND (full_name IS NOT NULL AND full_name != '')
    AND (full_name LIKE '%kath%' OR id = 12 OR id = 0)   
    ORDER BY full_name ASC LIMIT 15

(That query was auto-generated, hence the strange parentheses.)

The table has a few inidices:

+-----------+------------+------------------------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table   | Non_unique | Key_name                               | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-----------+------------+------------------------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| mytable |          0 | PRIMARY                                |            1 | id          | A         |     1329001 |     NULL | NULL   |      | BTREE      |         |               |
| mytable |          1 | mytable_by_agent_id                    |            1 | agent_id    | A         |        9561 |     NULL | NULL   | YES  | BTREE      |         |               |
| mytable |          1 | mytable_by_brand                       |            1 | brand_id    | A         |        4598 |     NULL | NULL   | YES  | BTREE      |         |               |
| mytable |          1 | mytable_by_full_name                   |            1 | full_name   | A         |     1329001 |     NULL | NULL   | YES  | BTREE          |         |               |
| mytable |          1 | index_mytable_on_brand_id_and_agent_id |            1 | brand_id    | A         |          11 |     NULL | NULL   | YES  | BTREE      |         |               |
| mytable |          1 | index_mytable_on_brand_id_and_agent_id |            2 | agent_id    | A         |        1205 |     NULL | NULL   | YES  | BTREE      |         |               |
+-----------+------------+------------------------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+

And I ‘d expect the query to make use of index_mytable_on_brand_id_and_agent_id, but instead, EXPLAIN reveals that it’s using mytable_by_full_name:

*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: consumers
         type: range
possible_keys: PRIMARY,mytable_by_agent_id,mytable_by_brand,mytable_by_full_name,index_mytable_on_brand_id_and_agent_id
          key: mytable_by_full_name
      key_len: 258
          ref: NULL
         rows: 811112
        Extra: Using where

How can I get this to use the other index? Why did it choose an index which won’t help (because of the % at the start of the LIKE)?

Answer :

No index will help with the WHERE clause — part has OR, part has LIKE with an initial wild card.

So, the only hope for optimizing is to work on the ORDER BY. However, that is risky — it might finish after looking at 15 rows, or it might look at the entire table without finding 15 rows. (Or some amount in between).

A single OR can usually be turned into UNION to good advantage. But 2 ORs is too messy to contemplate.

Leave a Reply

Your email address will not be published. Required fields are marked *