mysql - 加入索引字段不使用索引
问题描述
我有两张桌子。我已经编写了一个查询来将它们加入一列。该列在两个表中都有索引,但 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 |
+----+-------------+-------+------+---------------+------+---------+---------------------+-------+-------------------------+
解决方案
阻止使用索引的是字符集。
其中一个 zip 列是 latin1,另一个是 utf8。
有一个隐式转换,相当于CONVERT(zip USING charset)
一方面,
推荐阅读
- c# - 尝试单击特定 x 和 y 坐标时出现“移动目标越界”异常
- c++ - list::insert 是否会破坏 C++ 中的迭代器?
- google-chrome - 打开本地 HTML 文件:Chrome 将空间视为新标签?
- html - Flex Box 中的 SVG 在 Chrome 中消失
- javascript - 最小化条件语句的代码
- awk - 如果在文件的行中找到某个值,则使用 AWK 打印相邻字段
- python - PyQt4 QSqlDatabase:未加载 QMYSQL 驱动程序
- c++ - 在 dlmopen 之后使用 dlsym 时出现分段错误,但使用 dlopen dlsym 工作正常
- react-native - 为什么我使用 `useRef` 创建的我的参考会产生此错误消息?
- google-apps-script - LanguageApp.translate 限制