在表间连接

PostgreSQL 9.4.4 中文手册
上一页 | 上一级 | 章 2. SQL语言 | 下一页

# 2.6. 在表间连接

到目前为止，我们的查询一次只访问了一个表。查询可以一次访问多个表， 或者用某种方式访问一个表，而同时处理该表的多个行。一个同时访问同一个或 者不同表的多个行的查询叫连接查询。举例来说，比如你 想列出所有天气记录以及这些记录相关的城市。要实现这个目标，我们需要拿 `weather`表每行的`city`字段和`cities` 表所有行的`name`字段进行比较，并选取那些这些数值相匹配的行。

> 注意: 这里只是一个概念上的模型。连接通常以比实际比较每个可能的配对行更高效 的方式执行，但这些是用户看不到的。

这个任务可以用下面的查询来实现：

    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`表里的不匹配行。我们稍后将 看到如何修补这个问题。

- 有两个字段包含城市名。这是正确的，因为`weather`和 `cities`表的字段是接在一起的。不过，实际上我们不想要这些， 因此你将可能希望明确列出输出字段而不是使用`*`：

      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)

这个查询是一个左外连接，因为连接操作符(LEFT OUTER JOIN) 左边的表中的行在输出中至少出现一次，而右边的表只输出那些与左边的表中的某些行匹 配的行。如果输出的左表中的行没有右表中的行与其对应，那么右表中的字段将填充为 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, 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 表重新标记为`W1`和`W2`以区分连接的左边和右边。 你还可以用这样的别名在其它查询里节约一些敲键，比如：

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

以后会经常碰到这样的缩写。

上一页 | 起始页 | 下一页
查询一个表 | 上一级 | 聚合函数