使用或在MySQL查询中使用了错误的索引

使用或在MySQL查询中使用了错误的索引,mysql,indexing,explain,multiple-columns,Mysql,Indexing,Explain,Multiple Columns,我在MySQL查询中遇到了一个问题,它使用了错误的(低效的)索引 下表: mysql> describe ADDRESS_BOOK; +---------------+--------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +---------------+--------------+--

我在MySQL查询中遇到了一个问题,它使用了错误的(低效的)索引

下表:

mysql> describe ADDRESS_BOOK;
+---------------+--------------+------+-----+---------+----------------+
| Field         | Type         | Null | Key | Default | Extra          |
+---------------+--------------+------+-----+---------+----------------+
| ADD_BOOK_ID   | bigint(20)   | NO   | PRI | NULL    | auto_increment |
| COMPANY_ID    | bigint(20)   | NO   | MUL | NULL    |                |
| ADDRESS_NAME  | varchar(150) | NO   | MUL | NULL    |                |
| CLEAN_NAME    | varchar(150) | NO   | MUL | NULL    |                |
| ADDRESS_KEY_1 | varchar(150) | NO   | MUL | NULL    |                |
| ADDRESS_KEY_2 | varchar(150) | NO   | MUL | NULL    |                |
+---------------+--------------+------+-----+---------+----------------+
CLEAN_NAME是普通地址名称的“cleaned”版本,其中除[a-zA-Z]外的所有内容均已删除,ADDRESS_KEY1和ADDRESS_KEY2是ADDRESS_NAME中最长的两个词,除[a-zA-Z]外的所有内容均已删除

以下是我的索引(尝试寻找最佳索引):

现在我的问题是:

select * from ADDRESS_BOOK addressboo0_ 
where (addressboo0_.CLEAN_NAME like concat('trad', '%') 
or addressboo0_.ADDRESS_KEY_1 like concat('trad', '%') 
or addressboo0_.ADDRESS_KEY_2 like concat('trad', '%')) 
and addressboo0_.COMPANY_ID=1 
order by addressboo0_.CLEAN_NAME asc 
limit 200
系统中有来自不同公司的用户,因此查询应仅返回用户所在公司的通讯簿条目

对此的解释是

+----+-------------+--------------+------+----------------------------------------------------------------------+-------------------+---------+-------+------+-----------------------------+
| id | select_type | table        | type | possible_keys                                                        | key               | key_len | ref   | rows | Extra                       |
+----+-------------+--------------+------+----------------------------------------------------------------------+-------------------+---------+-------+------+-----------------------------+
|  1 | SIMPLE      | addressboo0_ | ref  | FK_ADDRESS_BOOK_2,FX_ADDRESS_KEYS,FK_ADDRESS_2,FK_CLEAN,FK_ADDRESS_1 | FK_ADDRESS_BOOK_2 | 8       | const | 4108 | Using where; Using filesort |
+----+-------------+--------------+------+----------------------------------------------------------------------+-------------------+---------+-------+------+-----------------------------+
我知道MySQL不能在或查询上使用多列索引,但正如您所看到的,它使用的是公司索引(FK_ADDRESS_BOOK_2),而不是字符串列的任何索引

如果我将公司从查询中删除,它将使用其他索引:

+----+-------------+--------------+-------------+----------------------------------------------------+------------------------------------+-------------+------+------+-----------------------------------------------------------------------------------+
| id | select_type | table        | type        | possible_keys                                      | key                                | key_len     | ref  | rows | Extra                                                                             |
+----+-------------+--------------+-------------+----------------------------------------------------+------------------------------------+-------------+------+------+-----------------------------------------------------------------------------------+
|  1 | SIMPLE      | addressboo0_ | index_merge | FX_ADDRESS_KEYS,FK_ADDRESS_2,FK_CLEAN,FK_ADDRESS_1 | FK_CLEAN,FK_ADDRESS_1,FK_ADDRESS_2 | 452,452,452 | NULL | 1089 | Using sort_union(FK_CLEAN,FK_ADDRESS_1,FK_ADDRESS_2); Using where; Using filesort |
+----+-------------+--------------+-------------+----------------------------------------------------+------------------------------------+-------------+------+------+-----------------------------------------------------------------------------------+
如果我对不同的公司使用相同的查询(包括公司),它会突然使用多列索引:

+----+-------------+--------------+-------+----------------------------------------------------------------------+-----------------+---------+------+------+-------------+
| id | select_type | table        | type  | possible_keys                                                        | key             | key_len | ref  | rows | Extra       |
+----+-------------+--------------+-------+----------------------------------------------------------------------+-----------------+---------+------+------+-------------+
|  1 | SIMPLE      | addressboo0_ | index | FK_ADDRESS_BOOK_2,FX_ADDRESS_KEYS,FK_ADDRESS_2,FK_CLEAN,FK_ADDRESS_1 | FX_ADDRESS_KEYS | 1364    | NULL |  492 | Using where |
+----+-------------+--------------+-------+----------------------------------------------------------------------+-----------------+---------+------+------+-------------+
因此,对于公司1,它有266个结果,而对于公司16,它有437个结果。公司1总共有4109个条目,而公司16有7745个条目

所以我很困惑。为什么MySQL对一家公司使用多列索引FX_ADDRESS_键,而对另一家公司使用效率相当低的FK_ADDRESS_BOOK_2键(基本上是对该公司的每一行进行检查)


如何改进查询/索引?如果我删除地址键1和地址键2的or,它将使用FX\U地址键索引,但我无法搜索名称内的字符串。如果我使用“%trade%”之类的内容,则无法使用任何索引。

如果您希望对此查询有一个漂亮的解释计划,请尝试以下操作:

CREATE INDEX FX_ADDRESS_KEYS_XX  ON ADDRESS_BOOK( 
         COMPANY_ID, 
         CLEAN_NAME, 
         ADDRESS_KEY_1, 
         ADDRESS_KEY_2 );
此索引应该可以改进查询,但要付出一定的代价。
它包含几乎整个表的副本(除了两列:
ADD\u BOOK\u ID bigint(20)
ADDRESS\u NAME varchar(150)
),这将占用大量磁盘空间。

而且它确实会减慢插入和更新的速度,因为索引数据也必须更新。

如果您想对此查询有一个漂亮的解释计划,请尝试以下方法:

CREATE INDEX FX_ADDRESS_KEYS_XX  ON ADDRESS_BOOK( 
         COMPANY_ID, 
         CLEAN_NAME, 
         ADDRESS_KEY_1, 
         ADDRESS_KEY_2 );
此索引应该可以改进查询,但要付出一定的代价。
它包含几乎整个表的副本(除了两列:
ADD\u BOOK\u ID bigint(20)
ADDRESS\u NAME varchar(150)
),这将占用大量磁盘空间。

而且它确实会减慢插入和更新的速度,因为索引数据也必须更新。

我一开始确实有那个索引,但后来将它更改为在最后有公司ID。当我运行explain时,它说它使用了新的索引,但突然访问的行数从492上升到14906。什么?但是!查询速度更快!你能解释一下为什么这个指数比我最后有公司ID的指数好吗?我认为订单很重要,对公司的限制在查询的末尾。实际上,我一开始有那个索引,但后来改为在末尾有公司ID。当我运行explain时,它说它使用了新的索引,但突然访问的行数从492上升到14906。什么?但是!查询速度更快!你能解释一下为什么这个指数比我最后有公司ID的指数好吗?我认为订单很重要,对公司的限制在问题的末尾。