选择 打开 改范围 完整检索页
受支持版本: 当前版本 (18) / 17 / 16 / 15 / 14
开发版本: 19 / devel
不受支持的版本: 13 / 12 / 11 / 10
当前 PostgreSQL 版本不在支持生命周期内。
您可以参阅当前版本的对应页面,或其他在上面列出的活跃大版本。

2.6. 表之间的连接 #

到目前为止,我们的查询每次只访问一个表。查询可以同时访问多个表,也可以访问同一个表并同时处理其中的多行。一次访问同一个表或不同表中多行的查询称为连接查询。例如,假设要列出所有天气记录及其对应城市的位置。为此,需要比较city列在weather表各行中的值与name列在cities表所有行中的值,选出这些值匹配的行对。

Note

这只是一个概念模型。连接通常会以比真正比较每一个可能的行对更高效的方式执行, 不过这对用户是不可见的。

下面的查询可以完成这一点:

SELECT *
    FROM weather, cities
    WHERE 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表中 的不匹配行。我们很快就会看到如何修正这一点。

  • 有两列包含城市名称。这是正确的,因为来自weathercities表的列列表被拼接在一起。不过,实际中通常不希望这样,因此可能更希望显式列出输出列,而不使用*

    SELECT city, temp_lo, temp_hi, prcp, date, location
        FROM weather, cities
        WHERE city = name;
    

练习:. 试着确定省略WHERE子句时,这条查询的含义。

由于所有列的名称各不相同,解析器自动确定了它们所属的表。如果两个表中有重复的列名,就需要限定列名,以说明所指的是哪一列,例如:

SELECT weather.city, weather.temp_lo, weather.temp_hi,
       weather.prcp, weather.date, cities.location
    FROM weather, cities
    WHERE cities.name = weather.city;

通常认为,限定连接查询中的所有列名是一种良好风格,这样即使以后向某个表中添加了重复列名,查询也不会失败。

目前所见的连接查询,也可以采用下面这种形式编写:

SELECT *
    FROM weather INNER JOIN cities ON (weather.city = cities.name);

这种语法不如上面的语法常用,不过这里展示它是为了帮助理解后续主题。

现在来看看如何把 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_lotemp_hi两列在每个weather行中的值与temp_lotemp_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, weather W2
    WHERE 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 表重新标记为W1W2,以区分连接的左侧和右侧。在其他查询中也可以使用这种别名来减少输入,例如:

SELECT *
    FROM weather w, cities c
    WHERE w.city = c.name;

你会经常遇到这种缩写方式。