到目前为止,我们的查询一次只能访问一个表。查询可以一次访问多个表,或者以这样的方式访问同一个表,即一次处理该表的多个行。一次访问多个表(或同一个表的多个实例)的查询称为连接查询。它们通过一个指定要配对哪些行的表达式,将一个表中的行与第二个表中的行结合起来。例如,要连同相关城市的所在地一起返回所有天气记录,则数据库需要将city
表中每个行的weather
列与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)
我们关注结果集中的两点
没有海沃德市的結果行。这是因为cities
表中没有海沃德的匹配条目,因此连接会忽略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)代替右表格列。
练习: 还有右外部连接和完整外部连接。尝试了解它们的作用。
我们还可以将一个表格与其自身连接。这称为 自连接。例如,假设我们希望找到温度范围内的所有天气记录。因此,我们需要将每个 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)
在此,我们已将天气表格重新标记为 w1
和 w2
,以便能够区分连接的左/右侧。您还可以在其他查询中使用此类别名以节省一些输入,例如
SELECT * FROM weather w JOIN cities c ON w.city = c.name;
您会经常碰到这么样的缩写风格。