支援的版本:目前 (17) / 16 / 15 / 14 / 13
開發版本:devel
不支援的版本:12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0 / 8.4 / 8.3 / 8.2 / 8.1 / 8.0 / 7.4 / 7.3 / 7.2

2.6. 表格之間的 Join #

到目前為止,我們的查詢一次只存取一個表格。查詢可以一次存取多個表格,或者以一次處理表格的多個列的方式存取同一個表格。一次存取多個表格(或同一個表格的多個實例)的查詢稱為 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 表格中不相符的列。我們稍後會看到如何修正此問題。

  • 有兩個欄位包含城市名稱。這是正確的,因為 weathercities 表格中的欄位清單已串連。不過,實際上這並不理想,因此您可能需要明確列出輸出欄位,而不是使用 *

    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;

這種語法早於 SQL-92 中引入的 JOIN/ON 語法。表格只是簡單地列在 FROM 子句中,而比較表示式會新增至 WHERE 子句。這種較舊的隱含語法和較新的明確 JOIN/ON 語法所產生的結果相同。但是對於查詢的讀者來說,明確語法更容易理解其含義:join 條件由其自己的關鍵字引入,而先前條件會與其他條件混合在 WHERE 子句中。

現在,我們將找出如何將 Hayward 記錄取回。我們希望查詢執行的是掃描 weather 表格,並為每一列找到相符的 cities 列。如果找不到相符的列,我們希望將一些空值替換為 cities 表格的欄位。這種查詢稱為 外部 join。(到目前為止我們所見的 join 是 內部 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)

此查詢稱為 左外部 join,因為 join 運算子左側提及的表格在輸出中至少會包含其每一列一次,而右側的表格只會輸出與左側表格的某列相符的列。當輸出左表格的列時,如果沒有右表格相符的列,則會將空值 (null) 替換為右表格的欄位。

練習:  還有右外部 join 和完整外部 join。嘗試找出它們的作用。

我們也可以將一個資料表與自身連接,這被稱為自我連接。舉例來說,假設我們想要找出所有溫度範圍與其他氣象紀錄重疊的氣象紀錄。因此,我們需要比較每個 weather 資料列的 temp_lotemp_hi 欄位,與所有其他 weather 資料列的 temp_lotemp_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 資料表重新命名為 w1w2,以便區分連接的左側和右側。您也可以在其他查詢中使用這種別名來節省一些打字,例如:

SELECT *
    FROM weather w JOIN cities c ON w.city = c.name;

您會經常遇到這種縮寫方式。



[4] 這只是一個概念模型。連接通常以比實際比較每一對資料列更有效率的方式執行,但這對使用者來說是不可見的。

提交更正

如果您在文件中發現任何不正確、與您的特定功能體驗不符或需要進一步澄清的地方,請使用此表單來報告文件問題。