2025年9月25日: PostgreSQL 18 釋出!
支援的版本:當前 (18) / 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 表中未匹配的行。我們很快就會看到如何解決這個問題。

  • 有兩個列包含城市名稱。這是正確的,因為 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;

普遍認為,在 JOIN 查詢中限定所有列名是一種良好的風格,這樣,如果將來在其中一個表中添加了重複的列名,查詢也不會失敗。

到目前為止看到的 JOIN 查詢也可以寫成這種形式:

SELECT *
    FROM weather, cities
    WHERE city = name;

此語法早於 JOIN/ON 語法,後者是在 SQL-92 中引入的。表只是簡單地列在 FROM 子句中,比較表示式被新增到 WHERE 子句中。這種舊的隱式語法和新的顯式 JOIN/ON 語法的 d 結果是相同的。但對於查詢的閱讀者來說,顯式語法使其含義更容易理解:JOIN 條件由其自己的關鍵字引入,而之前條件與 WHERE 子句中的其他條件混合在一起。

現在我們將弄清楚如何將 Hayward 的記錄也取回來。我們希望查詢執行的操作是掃描 weather 表,併為每一行找到匹配的 cities 行。如果沒有找到匹配的行,我們希望用一些 空值 替換 cities 表的列。這種查詢稱為 outer join。(我們到目前為止看到的 join 是 inner 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)

此查詢稱為 left outer join,因為在 JOIN 運算子左側提到的表中的每一行至少出現一次在輸出中,而右側的表只會輸出那些與左側表中的某些行匹配的行。當輸出一個左錶行但沒有右表匹配時,右表列將被替換為空(NULL)值。

練習: 還有 right outer join 和 full outer join。嘗試找出它們的用途。

我們還可以將一個表與自身 JOIN。這稱為 self 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,以便能夠區分 JOIN 的左側和右側。你也可以在其他查詢中使用這些型別的別名來節省一些打字,例如:

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

你會經常遇到這種縮寫風格。



[4] 這只是一個概念模型。JOIN 通常比實際比較每一對可能的行執行得更有效率,但這對使用者來說是不可見的。

提交更正

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