使用SELECT ALL进行二进制低级时不使用MySQL索引

时间:2014-03-04 19:52:26

标签: mysql sql indexing

我有一个keywords表,如下所示:

+---------+--------------+------+-----+---------+----------------+
| Field   | Type         | Null | Key | Default | Extra          |
+---------+--------------+------+-----+---------+----------------+
| id      | int(11)      | NO   | PRI | NULL    | auto_increment |
| name    | varchar(255) | YES  | MUL | NULL    |                |
| country | varchar(2)   | YES  |     | NULL    |                |
+---------+--------------+------+-----+---------+----------------+

我在[name, country]上有复合索引:

+----------+------------+------------------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+
| Table    | Non_unique | Key_name                           | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type |
+----------+------------+------------------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+
| keywords |          0 | PRIMARY                            |            1 | id          | A         |      377729 |     NULL | NULL   |      | BTREE      |
| keywords |          1 | index_keywords_on_name_and_country |            1 | name        | A         |      377729 |     NULL | NULL   | YES  | BTREE      |
| keywords |          1 | index_keywords_on_name_and_country |            2 | country     | A         |      377729 |     NULL | NULL   | YES  | BTREE      |
+----------+------------+------------------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+

我需要使用BINARY LOWER来比较name字段,因此我的查询将如下所示:

SELECT keywords.* FROM `keywords` WHERE (BINARY LOWER(`name`) = BINARY LOWER('Apple') AND `country` = 'US');

但问题是:它没有使用索引。使用Explain我有:

+------+-------------+----------+------+---------------+------+---------+------+--------+-------------+
| id   | select_type | table    | type | possible_keys | key  | key_len | ref  | rows   | Extra       |
+------+-------------+----------+------+---------------+------+---------+------+--------+-------------+
|    1 | SIMPLE      | keywords | ALL  | NULL          | NULL | NULL    | NULL | 366519 | Using where |
+------+-------------+----------+------+---------------+------+---------+------+--------+-------------+

但是,如果我选择某些字段,则代替select *,然后使用index:

Explain SELECT keywords.id, keywords.name FROM `keywords` WHERE (BINARY LOWER(`name`) = BINARY LOWER('Apple') AND `country` = 'US');

+------+-------------+----------+-------+---------------+------------------------------------+---------+------+--------+--------------------------+
| id   | select_type | table    | type  | possible_keys | key                                | key_len | ref  | rows   | Extra                    |
+------+-------------+----------+-------+---------------+------------------------------------+---------+------+--------+--------------------------+
|    1 | SIMPLE      | keywords | index | NULL          | index_keywords_on_name_and_country | 777     | NULL | 366519 | Using where; Using index |
+------+-------------+----------+-------+---------------+------------------------------------+---------+------+--------+--------------------------+

我正在使用MySQL 5.5。

出现这种情况的原因是什么?

有没有办法可以在查询中使用索引?或者如何更改我的查询和表以便使用索引来加速查询。

由于

2 个答案:

答案 0 :(得分:1)

为什么需要与binary lower()进行比较?对于关键字来说,这似乎是一个非常奇怪的要求。

无论如何,您可以使用子查询执行此操作:

SELECT k.*
FROM (SELECT k.*
      FROM `keywords` k
      WHERE name = 'Apple' and country = 'US'
     ) k
WHERE (BINARY LOWER(`name`) = BINARY LOWER('Apple') AND `country` = 'US');

内部子查询应该使用索引。生成的扫描应该在一个小子集上,因此它应该很快。

答案 1 :(得分:0)

是的,更改字符集(和排序规则)会破坏索引的使用。优化器不能依赖于指定字母顺序排列字符串的排序规则,就像它们存储在索引中一样,因此它不使用索引。

如果您使用不区分大小写的COLLATION,则根本不必执行此BINARY LOWER表达式。

mysql> select 'apple' = 'Apple';
+-------------------+
| 'apple' = 'Apple' |
+-------------------+
|                 1 |
+-------------------+

排序规则中的“ci”后缀表示不区分大小写。

mysql> show session variables like 'collation%';
+----------------------+-------------------+
| Variable_name        | Value             |
+----------------------+-------------------+
| collation_connection | utf8_general_ci   |
| collation_database   | latin1_swedish_ci |
| collation_server     | latin1_swedish_ci |
+----------------------+-------------------+

所以只需进行简单的字符串比较(假设您已将此表的排序顺序设置为ci排序规则):

SELECT keywords.* FROM `keywords` WHERE `name` = 'Apple' AND `country` = 'US');

重新评论:

比较重音字符取决于字符集和整理。

mysql> SELECT 'Lé' = 'le';

mysql> SET NAMES latin1 COLLATE latin1_general_ci;

mysql> select 'lé' = 'Lé';
+---------------+
| 'lé' = 'Lé'   |
+---------------+
|             1 |
+---------------+

mysql> select 'lé' = 'Le';
+--------------+
| 'lé' = 'Le'  |
+--------------+
|            0 |
+--------------+

我无法在MySQL中找到将重音字符视为不同的Unicode排序规则,但保留不区分大小写。

相关问题