6

MySQL 5.5.28。我有两个表PersonMessage后者有一个外键。每个表都有id一个主键列,并且该Person表还有一个personId(唯一)索引的列。

下面的查询应该利用personId键索引,但 MySQLMessage出于某种原因需要扫描整个表:

mysql> 解释 SELECT `m`.*
    -> 从
    -> `消息`作为`m`
    -> 左连接
    -> `Person` AS `p` ON (`m`.`person` = `p`.`id`)
    -> 在哪里
    -> 'M002649397' 为空或
    -> `p`.`personId` = 'M002649397';
+----+-------------+--------+--------+------------- --+---------+---------+----------------+--------+- ------------+
| 编号 | 选择类型 | 表| 类型 | 可能的键 | 关键 | key_len | 参考 | 行 | 额外 |
+----+-------------+--------+--------+------------- --+---------+---------+----------------+--------+- ------------+
| 1 | 简单 | 米 | 全部 | 空 | 空 | 空 | 空 | 273220 | |
| 1 | 简单 | p | eq_ref | 初级 | 初级 | 8 | pcom.m.人 | 1 | 使用位置 |
+----+-------------+--------+--------+------------- --+---------+---------+----------------+--------+- ------------+
2 行(0.00 秒)

但是当我注释掉'M002649397' IS NULL OR子句(对结果没有影响)时,查询突然变得更有效率:

mysql> 解释 SELECT `m`.*
    -> 从
    -> `消息`作为`m`
    -> 左连接
    -> `Person` AS `p` ON (`m`.`person` = `p`.`id`)
    -> 在哪里
    -> -- 'M002649397' 为空或
    -> `p`.`personId` = 'M002649397';
+----+-------------+--------+-------+-------------- ------+--------+---------+--------+---- --+--------------+
| 编号 | 选择类型 | 表| 类型 | 可能的键 | 关键 | key_len | 参考 | 行 | 额外 |
+----+-------------+--------+-------+-------------- ------+--------+---------+--------+---- --+--------------+
| 1 | 简单 | p | 常量 | PRIMARY,personId | 人名 | 第767章 常量 | 1 | 使用索引 |
| 1 | 简单 | 米 | 参考 | FK9C2397E7A0F6ED11 | FK9C2397E7A0F6ED11 | 9 | 常量 | 3 | 使用位置 |
+----+-------------+--------+-------+-------------- ------+--------+---------+--------+---- --+--------------+
2 行(0.01 秒)

我的问题是:为什么 MySQL 不够聪明,无法意识到这'M002649397' IS NULL总是错误的,优化它,省去不必要地扫描大表中的每一行?

换句话说,MySQL 优化器是否不知道它'M002649397' IS NULL总是错误的,或者它在构建其查询计划时未能将该优化应用于查询?

4

2 回答 2

1

实际上,更有趣的是,文档说 MySQL 足够聪明,可以做到这一点(参见此处)。

这似乎属于“8.2.1.2.消除“死”代码”的标题。

我想原因是开发人员在编写代码时没有考虑诸如“不为空”之类的表达式。该文档提供了许多基于常量传播(x1 = 2 and x2 = x1变得x1 = 2 and x2 = 2)的示例。 is null在这种情况下可能确实会出现。

于 2013-05-31T01:18:20.717 回答
1

这是一个经过验证的 MySQL 错误

您不能有一个条件的执行计划:

WHERE (0 = 1) 或 p.personId = 'string_constant';

和另一个执行计划:

WHERE p.personId = 'string_constant';

因为 (0 = 1) 总是导致 FALSE,这使得上述两个查询 100% 相同。

您可以在错误报告本身中看到,当 (0 = 1) OR 存在时的执行计划比表达式只是列与常量相等的执行计划差得多。

*请注意,这在 MariaDB 中已修复

于 2013-09-10T03:58:50.643 回答