这个自我加入是如何工作的?
How does this self join work?
这是来自sqlzoo.net
的问题
这里是 table 世界:
+-------------+-----------+---------+
| name | continent | area |
+-------------+-----------+---------+
| Afghanistan | Asia | 652230 |
| Albania | Europe | 2831741 |
| Algeria | Africa | 28748 |
| ... | ... | ... |
+-------------+-----------+---------+
问题:
找到每个大陆中最大的国家(按面积),显示大陆、名称和面积:
我试图理解的答案是:
SELECT continent, name, area
FROM world x
WHERE area >= ALL (SELECT area
FROM world y
WHERE y.continent=x.continent
AND area>0)
此代码给出:
+-------------+-----------+--------+
| continent | name | area |
+-------------+-----------+--------+
|Africa | Algeria | 2381741|
|Oceania | Australia | 7692024|
|South America| Brazil | 8515767|
|North America| Canada | 9984670|
|Asia | China | 9596961|
|Caribbean | Cuba | 109884|
|Europe | France | 640679|
|Eurasia | Russia |17125242|
+-------------+-----------+--------+
我不明白这是怎么回事。我认为内部 select 应该产生一个包含所有区域的 table 而外部 select 只选择最大的(>=
)。但它如何过滤到一个似乎按大陆分组的列表? y.continent=x.continent AND area>0
是如何工作的?
SELECT THIS, name, area
FROM world X
WHERE area >= ALL (SELECT area
FROM world y
WHERE y.continent = X.THIS
AND area > 0)
注意大写的 X
和 THIS
,它们是将子查询与查询联系在一起的项目。
在功能效果上(a),子查询在查询级别仅returns行与当前正在处理的行相关
所以这样想。在处理大陆Africa
时,子查询基本上是:
SELECT area
FROM world y
WHERE y.continent = 'Africa'
AND area > 0
而且,因为您在外部查询中有 WHERE area >= ALL [[that_sub_query]]
,它只会为您提供面积至少与该大陆最大面积一样大的行。
或者,更简洁地说,等于最大的,因为在给定的组中,没有一项可以同时小于另一项并同时大于或等于所有项。
(a) 它在幕后的工作方式可能 大 不同,但效果是我们在这里关心的。
为了解释,我假设世界 table 只有 5 个国家,如下所示:
Algeria 2381741
Australia 7692024
South Africa 1221037
New Zealand 268021
/*And to make it a little interesting:*/
Algeria Twin 2381741
子查询与基本查询的每一行匹配,一次匹配一个。这就是所谓的相关子查询。尽管相关子查询运行良好,但它们通常被认为是危险的,因为如果优化器无法找出更有效、等效的结构,它们往往会产生较差的性能特征。
以下 table 说明了有关如何评估数据的逻辑视图。请注意,数据库的查询引擎 可能 能够在内部将计划转换为数学上等效的内容,但效率更高。
+-------------+--------------+--------+
| continent | name | area |
+-------------+--------------+--------+
|Africa | Algeria | 2381741| >= ALL( /*y.continent='Africa'*/
2381741, /*Alegria*/
1221037, /*South Africa*/
2381741) /*Alegria Twin*/
|Oceania | Australia | 7692024| >= ALL( /*y.continent='Oceania'*/
7692024, /*Australia*/
268021) /*New Zealand*/
|Africa | South Africa | 1221037| >= ALL( /*y.continent='Africa'*/
2381741, /*Alegria*/
1221037, /*South Africa*/
2381741) /*Alegria Twin*/
|Oceania | New Zealand | 268021| >= ALL( /*y.continent='Oceania'*/
7692024, /*Australia*/
268021) /*New Zealand*/
|Africa | Algeria Twin | 2381741| >= ALL( /*y.continent='Africa'*/
2381741, /*Alegria*/
1221037, /*South Africa*/
2381741) /*Alegria Twin*/
+-------------+--------------+--------+
由上可知,第1、2、5行是>=
所有的子查询区域。所以这些被保留,而其他行被丢弃。
请注意,有几种方法可以编写将产生完全相同结果的子查询。
成为>=
大陆上的所有区域等同于成为=
大陆上的最大区域。
WHERE area = ( SELECT MAX(y.area)
FROM world y
WHERE y.continent=x.continent)
另一种获取最大值的方法是在按区域降序排序时获取第一行。
WHERE area = ( SELECT y.area
FROM world y
WHERE y.continent=x.continent
ORDER BY y.area DESC LIMIT 1)
但是,请注意以下看似等效但实际上并非等效的内容。
/* The problem here is that only 1 Algeria will happen to be
first in the sub-query. Meaning 1 row will be missing from
the final result set. */
WHERE name = ( SELECT y.name
FROM world y
WHERE y.continent=x.continent
ORDER BY y.area DESC LIMIT 1)
最后,我提到关联子查询可能会有性能问题。因此,如果可以的话,通常建议考虑将相关子查询重写为直接连接到 FROM 子句中的子查询的子查询。例如
SELECT x.contient, x.name, x.area
FROM world x
INNER JOIN (
SELECT MAX(y.area) as max_area, y.continent
FROM world y
GROUP BY y.continent
) z ON
x.continent = z.continent
AND x.area = z.max_area
这是来自sqlzoo.net
的问题这里是 table 世界:
+-------------+-----------+---------+ | name | continent | area | +-------------+-----------+---------+ | Afghanistan | Asia | 652230 | | Albania | Europe | 2831741 | | Algeria | Africa | 28748 | | ... | ... | ... | +-------------+-----------+---------+
问题:
找到每个大陆中最大的国家(按面积),显示大陆、名称和面积:
我试图理解的答案是:
SELECT continent, name, area
FROM world x
WHERE area >= ALL (SELECT area
FROM world y
WHERE y.continent=x.continent
AND area>0)
此代码给出:
+-------------+-----------+--------+ | continent | name | area | +-------------+-----------+--------+ |Africa | Algeria | 2381741| |Oceania | Australia | 7692024| |South America| Brazil | 8515767| |North America| Canada | 9984670| |Asia | China | 9596961| |Caribbean | Cuba | 109884| |Europe | France | 640679| |Eurasia | Russia |17125242| +-------------+-----------+--------+
我不明白这是怎么回事。我认为内部 select 应该产生一个包含所有区域的 table 而外部 select 只选择最大的(>=
)。但它如何过滤到一个似乎按大陆分组的列表? y.continent=x.continent AND area>0
是如何工作的?
SELECT THIS, name, area
FROM world X
WHERE area >= ALL (SELECT area
FROM world y
WHERE y.continent = X.THIS
AND area > 0)
注意大写的 X
和 THIS
,它们是将子查询与查询联系在一起的项目。
在功能效果上(a),子查询在查询级别仅returns行与当前正在处理的行相关
所以这样想。在处理大陆Africa
时,子查询基本上是:
SELECT area
FROM world y
WHERE y.continent = 'Africa'
AND area > 0
而且,因为您在外部查询中有 WHERE area >= ALL [[that_sub_query]]
,它只会为您提供面积至少与该大陆最大面积一样大的行。
或者,更简洁地说,等于最大的,因为在给定的组中,没有一项可以同时小于另一项并同时大于或等于所有项。
(a) 它在幕后的工作方式可能 大 不同,但效果是我们在这里关心的。
为了解释,我假设世界 table 只有 5 个国家,如下所示:
Algeria 2381741
Australia 7692024
South Africa 1221037
New Zealand 268021
/*And to make it a little interesting:*/
Algeria Twin 2381741
子查询与基本查询的每一行匹配,一次匹配一个。这就是所谓的相关子查询。尽管相关子查询运行良好,但它们通常被认为是危险的,因为如果优化器无法找出更有效、等效的结构,它们往往会产生较差的性能特征。
以下 table 说明了有关如何评估数据的逻辑视图。请注意,数据库的查询引擎 可能 能够在内部将计划转换为数学上等效的内容,但效率更高。
+-------------+--------------+--------+
| continent | name | area |
+-------------+--------------+--------+
|Africa | Algeria | 2381741| >= ALL( /*y.continent='Africa'*/
2381741, /*Alegria*/
1221037, /*South Africa*/
2381741) /*Alegria Twin*/
|Oceania | Australia | 7692024| >= ALL( /*y.continent='Oceania'*/
7692024, /*Australia*/
268021) /*New Zealand*/
|Africa | South Africa | 1221037| >= ALL( /*y.continent='Africa'*/
2381741, /*Alegria*/
1221037, /*South Africa*/
2381741) /*Alegria Twin*/
|Oceania | New Zealand | 268021| >= ALL( /*y.continent='Oceania'*/
7692024, /*Australia*/
268021) /*New Zealand*/
|Africa | Algeria Twin | 2381741| >= ALL( /*y.continent='Africa'*/
2381741, /*Alegria*/
1221037, /*South Africa*/
2381741) /*Alegria Twin*/
+-------------+--------------+--------+
由上可知,第1、2、5行是>=
所有的子查询区域。所以这些被保留,而其他行被丢弃。
请注意,有几种方法可以编写将产生完全相同结果的子查询。
成为>=
大陆上的所有区域等同于成为=
大陆上的最大区域。
WHERE area = ( SELECT MAX(y.area)
FROM world y
WHERE y.continent=x.continent)
另一种获取最大值的方法是在按区域降序排序时获取第一行。
WHERE area = ( SELECT y.area
FROM world y
WHERE y.continent=x.continent
ORDER BY y.area DESC LIMIT 1)
但是,请注意以下看似等效但实际上并非等效的内容。
/* The problem here is that only 1 Algeria will happen to be
first in the sub-query. Meaning 1 row will be missing from
the final result set. */
WHERE name = ( SELECT y.name
FROM world y
WHERE y.continent=x.continent
ORDER BY y.area DESC LIMIT 1)
最后,我提到关联子查询可能会有性能问题。因此,如果可以的话,通常建议考虑将相关子查询重写为直接连接到 FROM 子句中的子查询的子查询。例如
SELECT x.contient, x.name, x.area
FROM world x
INNER JOIN (
SELECT MAX(y.area) as max_area, y.continent
FROM world y
GROUP BY y.continent
) z ON
x.continent = z.continent
AND x.area = z.max_area