2.6. 表之间的连接 #
到目前为止,我们的查询每次只访问一个表。查询也可以同时访问多个表,或者以某种方式访问同一个表,使得该表的多行在同一时间被处理。一次访问多个表(或同一个表的多个实例)的查询称为连接查询。它把一个表中的行与第二个表中的行组合起来,并用一个表达式指定哪些行应当配对。例如,要返回所有天气记录及其关联城市的位置,数据库需要将 city 列(来自
weather 表的每一行)与 name 列(来自 cities 表的所有行)进行比较,并选出这些值相匹配的行对。[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 匹配的项,所以连接会忽略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;
在连接查询中限定所有列名通常被认为是一种好的风格,这样即使以后某个表中又添加了同名列,查询也不会失败。
到目前为止看到的这种连接查询也可以写成下面这种形式:
SELECT *
FROM weather, cities
WHERE city = name;
这种语法早于 JOIN/ON 语法,后者是在
SQL-92 中引入的。各表只是简单地列在 FROM 子句中,而比较表达式则被加到 WHERE 子句中。这种较旧的隐式语法与较新的显式
JOIN/ON 语法得到的结果完全相同。但是对查询的读者来说,显式语法更容易理解其含义:连接条件由自己的关键字引出,而以前它是与
WHERE 子句中的其他条件混在一起的。
现在来看看怎样把 Hayward 的记录重新纳入结果中。我们希望这个查询扫描
weather 表,并为其中的每一行找到匹配的
cities 表行。如果找不到匹配行,我们就希望用一些“空值”来替代 cities 表的列。这种查询称为外连接。(我们到目前为止看到的连接都是内连接。)命令如下:
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)
这个查询称为左外连接,因为位于连接操作符左侧的表中,每一行在输出中至少都会出现一次,而右侧的表只有那些能与左侧表某一行匹配的行才会出现在输出中。当某个左侧表行在右侧表中没有匹配项时,其对应输出行中的右侧表列将以空值(null)填充。
练习:. 还有右外连接和全外连接。试着找出它们能做什么。
我们也可以把一个表与它自身连接。这称为自连接。例如,假设我们想找出那些温度范围落在其他天气记录温度范围之内的天气记录。这样我们就需要把 temp_lo 和 temp_hi 列(即每个 weather 表行中的这两列)与
temp_lo 和 temp_hi 列(即所有其他 weather 表行中的这两列)进行比较。我们可以用下面的查询实现这一点:
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,以便区分连接的左侧和右侧。你在其他查询中也可以用这类别名来少打一些字,例如:
SELECT *
FROM weather w JOIN cities c ON w.city = c.name;
你会经常看到这种缩写风格。
报告文档问题
阅读 上游文档. 通过 PostgreSQL 文档反馈表单.