对带有数字参数的 varchar 列进行查询时出现意外的索引扫描

Xup*_*eng 6 mysql

我有一个带有这样架构的测试表:

CREATE TABLE `indextest` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(10) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=latin1
Run Code Online (Sandbox Code Playgroud)

这是表中的行:

mysql [localhost] {msandbox} (test) > select * from indextest;
+----+--------+
| id | name   |
+----+--------+
|  3 | 111222 |
|  1 | hello  |
|  2 | world  |
|  4 | wow    |
+----+--------+
4 rows in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)

当我针对name带有字符串的列查询表时,它看起来不错:

mysql [localhost] {msandbox} (test) > explain select * from indextest where name='111222'\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: indextest
         type: ref
possible_keys: idx_name
          key: idx_name
      key_len: 13
          ref: const
         rows: 1
        Extra: Using where; Using index
1 row in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)

但是如果我使用数字作为查询参数,explain 显示查询优化器正在执行索引扫描:

mysql [localhost] {msandbox} (test) > explain select * from indextest where name=111222\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: indextest
         type: index
possible_keys: idx_name
          key: idx_name
      key_len: 13
          ref: NULL
         rows: 4
        Extra: Using where; Using index
1 row in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)

在其他一些情况下(在在线慢查询中发现),查询优化器甚至建议使用表扫描进行类似的查询。

我不明白为什么它的行为是这样的,但不仅仅是引发错误或自动将数字转换为字符串。

小智 4

我认为这是因为数字可以在文本中以多种方式表示。根据MySQL 如何使用索引的文档:

如果在不进行转换的情况下无法直接比较值,则不同列的比较可能会阻止使用索引。假设将数字列与字符串列进行比较。对于数字列中的给定值(例如 1),它可能与字符串列中的任意数量的值(例如“1”、“1”、“00001”或“01.e1”(原文如此))进行比较。这排除了对字符串列使用任何索引。

(我认为01.e1这是一个拼写错误,应该阅读0.1e1。)

由于数字并不等于一个且仅一个文本值,因此无法使用索引。

另一方面,同样的情况并不适用于相反的方向。数字的文本表示确实等于一个且仅一个数值。换句话说:

虽然 numeric1可以等同于文本'1', ' 1', '00001', 或'0.1e1',但
文本'1', ' 1', '00001', and '0.1e1'all 仅等同于 numeric 1

因此,如果您有一个数字列并将字符串值与其进行比较,则可以按您的预期使用索引。(真的,我刚刚尝试过。)