Navicat Blog

SQL 的半聯結(SEMI JOIN) 2024 年 10 月 15 日,由 Robert Gravelle 撰寫

大多數資料庫開發人員和管理員對標準的內部、外部、左方和右方聯結類型都很熟悉。雖然這些聯結可以使用 ANSI SQL 編寫,但還有其他類型的聯結是基於關係代數運算子,這些運算子在 SQL 中並沒有語法可以表示。今天我們將探討其中一種聯結類型:半聯結(SEMI JOIN)。下週我們將研究類似的反聯結。為了更好地理解這些類型的聯結是如何運作,我們將使用 Navicat Premium Lite 17 在 PostgreSQL 的 dvdrental 資料庫執行一些 SELECT 查詢。它是一個基於 MySQL Sakila 範例資料庫的免費資料庫。

半聯結的解釋

請想像一下,如果 ANSI SQL 支援半聯結,其語法可能會類似於 Cloudera Impala 語法擴展,即 LEFT SEMI JOIN 和 RIGHT SEMI JOIN。在這種情況下,使用半連接的查詢可能會如下所示:

SELECT *
FROM actor
LEFT SEMI JOIN film_actor USING (actor_id)

上述查詢將傳回所有參與電影演出的演員。需要注意的是,我們不希望結果中出現任何電影,也不希望同一演員出現多次。我們只希望每個演員在結果中出現一次(或零次)。「Semi」一詞源自拉丁語,翻譯為英語為「half」(一半)。因此,我們的查詢僅會實現「half the join」(聯結的一半),即是左半部分。在 SQL 中,我們可以使用兩種替代語法來完成半聯結:EXISTS 和 IN。

使用 EXISTS 進行半聯結

以下是使用 EXISTS 實現的半聯結的等效查詢:

SELECT *
FROM actor a
WHERE EXISTS (
  SELECT * 
  FROM film_actor fa
  WHERE a.actor_id = fa.actor_id
)

如果我們在 Navicat Premium Lite 17 中執行查詢,我們可以看到它的結果正如預期的那樣:

semi_join_exists (147K)

EXISTS 運算子不會使用聯結,而是檢查 film_actor 資料表中每位演員是否存在一行或多行記錄。由於有了 WHERE 子句,大多數資料庫能夠識別出我們正在執行的是 SEMI JOIN(半聯結),而不是普通的 EXISTS() 謂詞。

使用 IN 進行半聯結

IN 和 EXISTS 是完全等效的 SEMI JOIN 模擬,因此以下查詢在大多數資料庫中將產生與之前的 EXISTS 查詢完全相同的結果:

SELECT *
FROM actor
WHERE actor_id IN (
  SELECT actor_id FROM film_actor
)

以下是上述查詢及其在 Navicat Premium Lite 17 中的結果::

semi_join_in (157K)

EXISTS 被認為是更好的語法選擇(儘管稍微冗長一些)。

總結

在今天的文章中,我們學習了如何使用 ANSI SQL 語法模擬半聯結。除了在「正確性」方面是最佳解決方案外,使用「SEMI」JOIN 而非 INNER JOIN 還具有一些效能優勢,因為資料庫在找到第一個相符項目後可以立即停止尋找。

有興趣嘗試 Navicat Premium Lite 17 嗎?你可以下載 14 天的全功能免費試用版。它適用於 Windows、macOS 和 Linux 作業系統。

Navicat Blogs
Feed Entries
Blog Archives
Share