加入索引字段不使用索引

时间:2019-02-22 18:36:40

标签: mysql sql

我有两个桌子。我写了一个查询,将它们加入一列。这两个表都为该列建立了索引,但是MySQL没有使用索引。有人可以a)告诉我原因和b)告诉我如何使MySQL使用索引快速连接这些表。

第一张桌子:

CREATE TABLE `dol_msa_zip_assoc` (
  `pkey` int(5) unsigned NOT NULL AUTO_INCREMENT,
  `zip` varchar(5) NOT NULL DEFAULT '',
  `pmsa_msa` varchar(5) NOT NULL DEFAULT '',
  PRIMARY KEY (`pkey`),
  KEY `zip` (`zip`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8;

它包含42690条记录。

另一个表:

CREATE TABLE `v3_msa_zip_assoc` (
  `pkey` int(5) unsigned NOT NULL AUTO_INCREMENT,
  `zip` varchar(9) NOT NULL DEFAULT '',
  `pmsa_msa` varchar(6) NOT NULL DEFAULT '',
  PRIMARY KEY (`pkey`),
  KEY `zip` (`zip`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1;

它包含42486条记录。

我的查询旨在在第一个表中查找记录,而不是在第二个表中查找记录:

SELECT d.*, o.* FROM `dol_msa_zip_assoc` d
LEFT JOIN `v3_msa_zip_assoc` o
ON o.zip = d.zip
WHERE o.zip IS NULL

当我解释此查询时,发现未使用 zip 列上的索引:

+----+-------------+-------+------+---------------+------+---------+------+-------+----------------------------------------------------------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows  | Extra                                                          |
+----+-------------+-------+------+---------------+------+---------+------+-------+----------------------------------------------------------------+
|  1 | SIMPLE      | d     | ALL  | NULL          | NULL | NULL    | NULL | 42915 | NULL                                                           |
|  1 | SIMPLE      | o     | ALL  | NULL          | NULL | NULL    | NULL | 42486 | Using where; Not exists; Using join buffer (Block Nested Loop) |
+----+-------------+-------+------+---------------+------+---------+------+-------+----------------------------------------------------------------+

是什么原因导致此索引无法使用?是因为一个zip列是latin1,另一个是utf8?如何获得该查询以利用索引,这样就不需要花很多时间了?

编辑:反转JOIN的顺序显然确实利用了索引:

SELECT d . * , o . *
FROM `v3_msa_zip_assoc` o
LEFT JOIN `dol_msa_zip_assoc` d ON d.zip = o.zip
WHERE d.zip IS NULL

这是解释:

+----+-------------+-------+------+---------------+------+---------+------+-------+-------------------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows  | Extra                   |
+----+-------------+-------+------+---------------+------+---------+------+-------+-------------------------+
|  1 | SIMPLE      | o     | ALL  | NULL          | NULL | NULL    | NULL | 42486 | NULL                    |
|  1 | SIMPLE      | d     | ref  | zip           | zip  | 17      | func |     1 | Using where; Not exists |
+----+-------------+-------+------+---------------+------+---------+------+-------+-------------------------+

编辑2:我将旧表的结构更改为使用utf8归类。现在已定义如下:

CREATE TABLE `v3_msa_zip_assoc` (
  `pkey` int(5) unsigned NOT NULL AUTO_INCREMENT,
  `zip` varchar(9) NOT NULL DEFAULT '',
  `pmsa_msa` varchar(6) NOT NULL DEFAULT '',
  PRIMARY KEY (`pkey`),
  KEY `zip` (`zip`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8;

这似乎已经解决了问题:

mysql> EXPLAIN SELECT d . * , o . *
    -> FROM `dol_msa_zip_assoc` d
    -> LEFT JOIN `v3_msa_zip_assoc` o ON o.zip = d.zip
    -> WHERE o.zip IS NULL;
+----+-------------+-------+------+---------------+------+---------+---------------------+-------+-------------------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref                 | rows  | Extra                   |
+----+-------------+-------+------+---------------+------+---------+---------------------+-------+-------------------------+
|  1 | SIMPLE      | d     | ALL  | NULL          | NULL | NULL    | NULL                | 42915 | NULL                    |
|  1 | SIMPLE      | o     | ref  | zip           | zip  | 29      | myplan_v4_dev.d.zip |     1 | Using where; Not exists |
+----+-------------+-------+------+---------------+------+---------+---------------------+-------+-------------------------+

1 个答案:

答案 0 :(得分:6)

是字符集阻止了索引的使用。

其中一个zip列是latin1,另一个是utf8。

有一个隐式转换,等效于一侧的CONVERT(zip USING charset)