MySQL 5.5.28。我有两个表Person
和Message
,后者有前一个外键。每个表都有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,或者在构造查询计划时是否未能将该优化应用于查询?
答案 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)
您不能为条件制定一个执行计划:
WHERE(0 = 1)或p.personId ='string_constant';
和另一个执行计划:
WHERE p.personId ='string_constant';
因为(0 = 1)总是导致FALSE,这使得上面两个查询100%相同。
你可以在错误报告本身看到,当(0 = 1)OR存在时执行计划比表达式只是列与常量相等的情况要差得多。
*请注意,这是fixed in MariaDB。