在多个连接中放置“开启”条件在哪里更好? (mysql)
Posted
技术标签:
【中文标题】在多个连接中放置“开启”条件在哪里更好? (mysql)【英文标题】:Where is better to put 'on' conditions in multiple joins? (mysql) 【发布时间】:2013-08-12 16:44:09 【问题描述】:我有多个连接,包括 mysql 中的左连接。有两种方法可以做到这一点。
我可以在每次加入后立即设置“ON”条件:
select * from A join B ON(A.bid=B.ID) join C ON(B.cid=C.ID) join D ON(c.did=D.ID)
我可以将它们全部放在一个“ON”子句中:
select * from A join B join C join D ON(A.bid=B.ID AND B.cid=C.ID AND c.did=D.ID)
哪种方式更好?
如果我在查询中需要左连接或右连接,会有什么不同吗?
【问题讨论】:
【参考方案1】:对于简单的用途,MySQL 几乎不可避免地会以相同的方式执行它们,因此这是一种偏好和可读性的方式(这是一个很大的争论主题)。
但是,对于更复杂的查询,特别是具有可能成为磁盘和 io 绑定的 OUTER JOIN
s 的聚合查询 - 不使用带有 OUTER JOIN 查询的 WHERE 子句可能会产生性能和不可见的影响。
运行 8 分钟或 0.8 秒的查询之间的差异可能最终取决于 WHERE
子句,尤其是与索引有关的 (How MySQL uses Indexes):@987654326 @ 子句是为查询优化器提供完成工作所需的信息并告诉引擎如何以最有效的方式执行查询的核心部分。
来自How MySQL Optimizes Queries using WHERE:
"本节讨论可用于处理的优化 WHERE 子句...连接表的最佳连接组合是 通过尝试所有的可能性找到。如果 ORDER BY 中的所有列和 GROUP BY 子句来自同一张表,该表是首选 加入时先。”
对于连接中的每个表,构造一个更简单的 WHERE 以获得快速 对表进行 WHERE 评估,并尽快跳过行 可能
一些例子:
全表扫描(类型 = ALL),在 EXTRA 中没有 Using where
[SQL] SELECT cr.id,cr2.role FROM CReportsAL cr
LEFT JOIN CReportsCA cr2
ON cr.id = cr2.id AND cr.role = cr2.role AND cr.util = 1000
[Err] Out of memory
使用where
优化结果,带索引(Using where
,Using index
):
[SQL] SELECT cr.id,cr2.role FROM CReportsAL cr
LEFT JOIN CReportsCA cr2
ON cr.id = cr2.id
WHERE cr.role = cr2.role
AND cr.util = 1000
515661 rows in set (0.124s)
****ON/WHERE 的组合 - 相同的结果 - EXPLAIN
******* 中的相同计划
[SQL] SELECT cr.id,cr2.role FROM CReportsAL cr
LEFT JOIN CReportsCA cr2
ON cr.id = cr2.id
AND cr.role = cr2.role
WHERE cr.util = 1000
515661 rows in set (0.121s)
MySQL 通常足够聪明,可以找出像上面这样的简单查询,并且会以类似的方式执行它们,但在某些情况下它不会。
外连接查询性能:
由于 LEFT JOIN 和 RIGHT JOIN 都是 OUTER JOIN(Great in depth review here) 出现笛卡尔积的问题,因此必须避免表扫描,以便尽可能快地消除查询不需要的行尽可能。
WHERE
,索引和查询优化器一起使用可能完全消除笛卡尔积所带来的问题,当仔细使用聚合函数如AVERAGE
、GROUP BY
、SUM
、@ 987654339@ 等。通过用户适当的索引和 WHERE 子句的使用,运行时间减少了几个数量级。
终于
同样,对于大多数查询,查询优化器将以相同的方式执行它们 - 使其成为一种偏好方式,但是当查询优化变得重要时,WHERE
是一个非常重要的工具。通过将索引 col 指定为附加的 ON..AND ON 子句,我在 INNER JOIN
的某些情况下看到了一些性能提升,但我无法告诉你原因。
【讨论】:
cerd - 这是很棒的东西,正如我在另一条评论中提到的那样,我总是很高兴学到一些东西。当您将示例查询重新转换为SELECT cr.id,cr2.role FROM CReportsAL cr LEFT JOIN CReportsCA cr2 ON cr.id = cr2.id AND cr.role = cr2.role WHERE cr.util = 1000
时会发生什么?
@EdGibbs - 好电话,正在使用我自己的公司。 db 并更改了表/字段并将该行留在了第二个示例之外。将添加另一个混合动力车。
谢谢@cerd - 我很欣赏这些例子!
@EdGibbs - 运行您的示例,相同的计划,相同的配置文件/解释结果(除了 cr2 表仅使用索引,没有位置)同时。有趣的是,对于内存爆炸的查询,它还报告它正在使用索引——唯一的区别是查询中的Using where
为两者都完成了。不知道如何解释——我在 MySQL 的 Percona 版本上运行,它在一些方面有更好的优化。【参考方案2】:
将 ON 子句与其适用的 JOIN 放在一起。
原因是:
可读性:其他人可以轻松查看表的连接方式 性能:如果您稍后在查询中保留条件,您将获得比需要更多的连接 - 这就像将条件放在 where 子句中 约定:遵循常规样式,您的代码将更易于移植,并且不太可能遇到异常语法可能出现的问题 - 行之有效【讨论】:
以上是关于在多个连接中放置“开启”条件在哪里更好? (mysql)的主要内容,如果未能解决你的问题,请参考以下文章
在 SwiftUI 应用程序中放置音频播放代码的位置[关闭]