2022 年 11 月 11 日
,由 Robert Gravelle 撰写
在
联接与子查询:哪个更快?
文章我们了解到连接往往比子查询执行得更快。话虽如此,这并不是一条通用准则,因此你可能不希望自动假设联接更可取。正如那篇文章中提到的,如果你需要在查询添加许多联接,数据库服务器就必须做更多的工作,这可能会导致数据检索时间变慢。本文将介绍几个你可以执行的快速测试,比较使用联接的查询和包含子查询的查询,让你可以选择最佳性能的查询。
两个查询,相同结果
大多数时候,你可以使用连接或子查询来编写查询。为了说明这一点,以下有一个从
MySQL Sakila 示例数据库
中选择地区及其相关城市和地址的查询。第一个 SELECT 语句使用联接,而第二个 SELECT 语句使用子查询获取完全相同的数据:
SELECT
co.Country,
COUNT(DISTINCT ci.city_id) AS city_cnt,
COUNT(a.city_id) AS address_cnt
FROM country co
INNER JOIN city ci
ON co.country_id = ci.country_id
INNER JOIN address a
ON ci.city_id = a.city_id
GROUP BY
co.country_id;
SELECT
Co.Country,
(Select COUNT(1)
FROM City Ci
WHERE Ci.country_id=co.country_id) AS city_cnt,
(Select COUNT(1)
FROM Address A
INNER JOIN city c on a.city_id=c.city_id
WHERE C.country_id=co.country_id) AS address_cnt
From Country Co;
在
Navicat
中,我们可以很容易地比较查询结果,因为它可以同时运行多个查询。每个结果集都显示在 SQL 编辑器下的一个选项卡中。在下图中,
结果 2
选项卡的内容显示在
结果 1
旁边以便快速比较:
查询执行时间
验证了这两个语句是等效的之后,我们现在可以比较它们的执行时间。
为此,我们可以选择其中一个语句,然后点击“
运行
”按钮。只要在编辑器中选择了文本,该按钮的标签就会変为“
运行已选择的
”。在画面底部可以看到
运行时间
是 0.020秒:
对第二条语句执行相同操作,得出
运行时间
为
0.021秒
。时间差异很小,但会随着数据量的增加而增长:
比较执行计划
一个查询的执行计划可以揭示很多关于它执行速度的信息。在 Navicat 中,我们可以点击“
解释
”按钮来查看执行计划。 虽然需要一些练习才能熟练地解译解释的结果,但这样做可以在尝试确定查询的效率时带来好处。
解释 1
选项卡显示第一个(联接)查询的执行计划。我们可以一眼看出它涉及 3 个 SIMPLE 选择:
同時,
解释 2
选项卡列出了一个 PRIMARY 选择,然后是三个 DEPENDENT SUBQUERY。即使不深入探究,我们也可以看到执行第二个(子查询)语句需要一个额外的步骤: