到目前为止,我们的查询一次只访问一个表。查询可以一次访问多个表,或者以处理该表的多行的方式访问同一个表。一次访问多个表(或同一个表的多个实例)的查询称为 JOIN 查询。它们将一个表的行与第二个表的行组合起来,并使用一个表达式指定要配对的行。例如,要返回所有天气记录以及相关城市的位置,数据库需要比较 weather
表的每一行的 city
列与 cities
表的所有行的 name
列,并选择这些值匹配的行对。[4] 这将通过以下查询完成:
SELECT * FROM weather JOIN cities ON city = name;
city | temp_lo | temp_hi | prcp | date | name | location ---------------+---------+---------+------+------------+---------------+----------- San Francisco | 46 | 50 | 0.25 | 1994-11-27 | San Francisco | (-194,53) San Francisco | 43 | 57 | 0 | 1994-11-29 | San Francisco | (-194,53) (2 rows)
注意结果集中的两点:
Hayward 市没有结果行。这是因为 cities
表中没有 Hayward 的匹配条目,因此 join 忽略了 weather
表中未匹配的行。我们很快就会看到如何解决这个问题。
有两个列包含城市名称。这是正确的,因为 weather
表和 cities
表的列列表被连接起来了。然而,在实际应用中这是不理想的,所以你可能希望显式列出输出列,而不是使用 *
。
SELECT city, temp_lo, temp_hi, prcp, date, location FROM weather JOIN cities ON city = name;
由于所有列的名称都不同,解析器会自动找到它们所属的表。如果两个表中有重复的列名,你需要 限定 列名来表明你指的是哪一个,例如:
SELECT weather.city, weather.temp_lo, weather.temp_hi, weather.prcp, weather.date, cities.location FROM weather JOIN cities ON weather.city = cities.name;
普遍认为,在 JOIN 查询中限定所有列名是一种良好的风格,这样,如果将来在其中一个表中添加了重复的列名,查询也不会失败。
到目前为止看到的 JOIN 查询也可以写成这种形式:
SELECT * FROM weather, cities WHERE city = name;
此语法早于 JOIN
/ON
语法,后者是在 SQL-92 中引入的。表只是简单地列在 FROM
子句中,比较表达式被添加到 WHERE
子句中。这种旧的隐式语法和新的显式 JOIN
/ON
语法的 d 结果是相同的。但对于查询的阅读者来说,显式语法使其含义更容易理解:JOIN 条件由其自己的关键字引入,而之前条件与 WHERE 子句中的其他条件混合在一起。
现在我们将弄清楚如何将 Hayward 的记录也取回来。我们希望查询执行的操作是扫描 weather
表,并为每一行找到匹配的 cities
行。如果没有找到匹配的行,我们希望用一些 “空值” 替换 cities
表的列。这种查询称为 outer join。(我们到目前为止看到的 join 是 inner join。)命令如下:
SELECT * FROM weather LEFT OUTER JOIN cities ON weather.city = cities.name;
city | temp_lo | temp_hi | prcp | date | name | location ---------------+---------+---------+------+------------+---------------+----------- Hayward | 37 | 54 | | 1994-11-29 | | San Francisco | 46 | 50 | 0.25 | 1994-11-27 | San Francisco | (-194,53) San Francisco | 43 | 57 | 0 | 1994-11-29 | San Francisco | (-194,53) (3 rows)
此查询称为 left outer join,因为在 JOIN 操作符左侧提到的表中的每一行至少出现一次在输出中,而右侧的表只会输出那些与左侧表中的某些行匹配的行。当输出一个左表行但没有右表匹配时,右表列将被替换为空(NULL)值。
练习: 还有 right outer join 和 full outer join。尝试找出它们的用途。
我们还可以将一个表与自身 JOIN。这称为 self join。例如,假设我们希望找到所有在其他天气记录温度范围内的天气记录。因此,我们需要将每个 weather
行的 temp_lo
和 temp_hi
列与所有其他 weather
行的 temp_lo
和 temp_hi
列进行比较。我们可以通过以下查询来做到这一点:
SELECT w1.city, w1.temp_lo AS low, w1.temp_hi AS high, w2.city, w2.temp_lo AS low, w2.temp_hi AS high FROM weather w1 JOIN weather w2 ON w1.temp_lo < w2.temp_lo AND w1.temp_hi > w2.temp_hi;
city | low | high | city | low | high ---------------+-----+------+---------------+-----+------ San Francisco | 43 | 57 | San Francisco | 46 | 50 Hayward | 37 | 54 | San Francisco | 46 | 50 (2 rows)
在这里,我们将 weather 表重命名为 w1
和 w2
,以便能够区分 JOIN 的左侧和右侧。你也可以在其他查询中使用这些类型的别名来节省一些打字,例如:
SELECT * FROM weather w JOIN cities c ON w.city = c.name;
你会经常遇到这种缩写风格。
如果您在文档中发现任何不正确之处,与您对特定功能的体验不符,或需要进一步澄清,请使用 此表单 报告文档问题。