我有一个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。
出现这种情况的原因是什么?
有没有办法可以在查询中使用索引?或者如何更改我的查询和表以便使用索引来加速查询。
由于
答案 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排序规则,但保留不区分大小写。