JOIN 条件和 WHERE 条件之间是否存在执行差异?

Bel*_*Bob 18 mysql performance oracle

这两个示例查询之间是否存在性能差异?

查询 1:

select count(*)
from   table1 a
join   table2 b
on     b.key_col=a.key_col
where  b.tag = 'Y'
Run Code Online (Sandbox Code Playgroud)

查询 2;

select count(*)
from   table1 a
join   table2 b
on     b.key_col=a.key_col
   and b.tag = 'Y'
Run Code Online (Sandbox Code Playgroud)

注意唯一的区别是补充条件的位置;第一个使用WHERE子句,第二个将条件添加到ON子句中。

当我在 Teradata 系统上运行这些查询时,解释计划是相同的,JOIN 步骤显示了每种情况下的附加条件。但是,在关于 MySQL 的这个 SO 问题上,其中一个答案表明首选第二种样式,因为WHERE在进行连接之后进行处理。

编码这样的查询时是否有一般规则要遵循?我猜它必须依赖于平台,因为它显然对我的数据库没有影响,但这也许只是 Teradata 的一个功能。而如果它与平台相关的,我非常喜欢弄几个文件的参考资料; 我真的不知道该找什么。

Rol*_*DBA 14

根据Sasha Pachev的《Understanding MySQL Internals》一书第 9 章(解析器和优化器)第 172 页

了解 MySQL 内部

以下是将查询评估分解为以下任务:

  • 确定哪些键可用于从表中检索记录,并为每个表选择最好的一个。
  • 对于每个表,决定表扫描是否比读取键更好。如果匹配key值的记录很多,key的优势就减少了,表扫描变快了。
  • 当查询中存在多个表时,确定应连接表的顺序。
  • 重写 WHERE 子句以消除死代码,减少不必要的计算并尽可能更改约束以打开使用密钥的方式。
  • 从连接中删除未使用的表。
  • 确定键是否可用于ORDER BYGROUP BY
  • 尝试简化子查询,并确定可以将其结果缓存到什么程度。
  • 合并视图(将视图引用扩展为宏)

在同一页面上,它说:

在 MySQL 优化器术语中,每个查询都是一组连接。术语连接在这里比 SQL 命令中使用的更广泛。只有一个表的查询是退化连接。虽然我们通常不认为从一张表中读取记录是一种连接,但与传统连接使用的相同结构和算法可以完美地解决只有一张表的查询。

结语

由于存在的键、数据量和查询的表达方式,MySQL Joins 有时可能会为我们自己(或报复我们)做一些事情,并得出我们没有预料到且无法快速解释的结果。

我之前写过关于这种怪癖的文章

因为 MySQL 查询优化器可以在查询评估期间关闭某些键。

@Phil 的评论帮助我了解如何发布此答案(@Phil 的评论为 +1)

@ypercube 的评论(这个评论也是 +1)是我帖子的精简版,因为 MySQL 的查询优化器是原始的。不幸的是,它必须是因为它处理外部存储引擎。

结论

至于您的实际问题,MySQL Query Optimizer 将在完成每个查询时确定其性能指标

  • 计算行数
  • 选择键
  • 按摩间歇性结果集
  • 哦,是的,做实际的 JOIN

您可能必须通过重写(重构)查询来强制执行顺序

这是您提供的第一个查询

select count(*)
from   table1 a
join   table2 b
on     b.key_col=a.key_col
where  b.tag = 'Y';
Run Code Online (Sandbox Code Playgroud)

尝试重写它以首先评估 WHERE

select count(*)
from   table1 a
join   (select key_col from table2 where tag='Y') b
on     b.key_col=a.key_col;
Run Code Online (Sandbox Code Playgroud)

那肯定会改变解释计划。它可能会产生更好或更坏的结果。

我曾经在 StackOverflow 中回答过一个问题,我在那里应用了这种技术。解释是可怕的,但表现是炸药。它只是因为存在正确的索引并且在子查询中使用了 LIMIT 才起作用

与股票价格一样,当涉及到查询并试图表达它们时,会受到限制,结果可能会有所不同,而且过去的表现并不代表未来的结果。

  • @Rolando:您可以添加关于最新 MariaDB(5.3 和 5.5)版本和最近发布的主要 MySQL (5.6) 版本中优化器改进的**后果**。这可能会使一些重写变得不必要。 (6认同)
  • +1 用于详细的 MySQL 特定信息,尤其是为了诱使我了解“尾声”和“结论”之间的区别! (2认同)