【问题标题】:MySQL: why isn't 'FOO' IS NULL optimized away?MySQL:为什么不优化'FOO'IS NULL?
【发布时间】:2013-05-26 17:42:57
【问题描述】:

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

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

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 始终为假,或者它在构造查询计划时未能将该优化应用于查询?

【问题讨论】:

  • 也许查询计划器不是这样设计的,因为他们希望开发人员不要编写我们可以轻松手动优化的查询?在查询中甚至有这个条件似乎有点傻。一个更好的问题可能是,“我为什么要在我的查询中使用这个?我能找到更好的方法来生成查询吗?”如果您正在生成查询代码端,那么您应该能够省略条件。
  • @jpmc26:实际上他们的优化器旨在执行此类优化dev.mysql.com/doc/refman/5.5/en/where-optimizations.html
  • 如果将where子句更改为WHERE 0 or `p`.`personId` = 'M002649397'会发生什么
  • 我想知道如果IS NULL被任何其他常量表达式替换,结果是否相同。

标签: mysql


【解决方案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

【讨论】:

  • OP 已经知道了....仔细看看错误报告....是他写的 :)
【解决方案2】:

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

这似乎在标题“8.2.1.2。消除“死”代码”下。

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

【讨论】:

  • 他提到0 or personid = 'xxx'也没有优化,所以与优化is not null无关。听起来他们没有将0 or <expression> 优化为<expression>
  • @Barmar 。 . . (嗨,Barmar,我想我们曾经一起工作过。)这不是我第一次发现 MySQL 文档比 MySQL 实现更乐观。
  • “MySQL 文档比 MySQL 实现更乐观” --- 文档驱动开发:您声明的功能比您拥有的更多,并在人们真正指出错误时立即实施它
  • @zerkms 。 . .在我写了那条评论之后,我意识到它可能会让人觉得太消极了。我遇到的唯一其他情况是聚合的索引优化。 group by 在实践中似乎没有使用索引,但根据文档进行。总的来说,我对文档印象深刻。 . .这偏离了 OP。
  • (是的,在 TMC)。非常令人沮丧——在简化表达式时优化身份是一项 CS 101 练习,任何体面的编译器都应该这样做。
猜你喜欢
  • 2012-03-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-04
  • 2018-11-01
  • 2018-08-11
  • 1970-01-01
相关资源
最近更新 更多