MySQL:为什么没有' FOO' IS NULL优化了吗?

时间:2013-05-31 00:52:33

标签: mysql

MySQL 5.5.28。我有两个表PersonMessage,后者有前一个外键。每个表都有id作为主键列,Person表也有一个personId列(唯一)索引。

下面的查询应该利用personId密钥索引,但MySQL需要扫描整个Message表,原因如下:

mysql> EXPLAIN SELECT `m`.*
    -> FROM
    ->   `Message` AS `m`
    -> LEFT JOIN
    ->   `Person` AS `p` ON (`m`.`person` = `p`.`id`)
    -> WHERE
    ->   'M002649397' IS NULL OR
    ->   `p`.`personId` = 'M002649397';
+----+-------------+-------+--------+---------------+---------+---------+----------------+--------+-------------+
| id | select_type | table | type   | possible_keys | key     | key_len | ref            | rows   | Extra       |
+----+-------------+-------+--------+---------------+---------+---------+----------------+--------+-------------+
|  1 | SIMPLE      | m     | ALL    | NULL          | NULL    | NULL    | NULL           | 273220 |             |
|  1 | SIMPLE      | p     | eq_ref | PRIMARY       | PRIMARY | 8       | pcom.m.person  |      1 | Using where |
+----+-------------+-------+--------+---------------+---------+---------+----------------+--------+-------------+
2 rows in set (0.00 sec)

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

mysql> EXPLAIN SELECT `m`.*
    -> FROM
    ->   `Message` AS `m`
    -> LEFT JOIN
    ->   `Person` AS `p` ON (`m`.`person` = `p`.`id`)
    -> WHERE
    -> --  'M002649397' IS NULL OR
    ->   `p`.`personId` = 'M002649397';
+----+-------------+-------+-------+--------------------+--------------------+---------+-------+------+-------------+
| id | select_type | table | type  | possible_keys      | key                | key_len | ref   | rows | Extra       |
+----+-------------+-------+-------+--------------------+--------------------+---------+-------+------+-------------+
|  1 | SIMPLE      | p     | const | PRIMARY,personId   | personId           | 767     | const |    1 | Using index |
|  1 | SIMPLE      | m     | ref   | FK9C2397E7A0F6ED11 | FK9C2397E7A0F6ED11 | 9       | const |    3 | Using where |
+----+-------------+-------+-------+--------------------+--------------------+---------+-------+------+-------------+
2 rows in set (0.01 sec)

我的问题是:为什么MySQL没有足够聪明地意识到'M002649397' IS NULL总是错误的,优化它,并且不必不必要地扫描巨大的表中的每一行?

换句话说,MySQL优化器是否不知道'M002649397' IS NULL始终为false,或者在构造查询计划时是否未能将该优化应用于查询?

2 个答案:

答案 0 :(得分:1)

实际上,更有趣的是,文档说MySQL非常聪明(参见here)。

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

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

答案 1 :(得分:1)

这是verified MySQL bug

您不能为条件制定一个执行计划:

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

和另一个执行计划:

WHERE p.personId ='string_constant';

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

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

*请注意,这是fixed in MariaDB