寫一個連接要先決定的是哪兩列該配在一起,而 SQL 給了這個決定一個專屬的位置:ON。本篇把這件事叫配對——關係還在組的那個過程,哪一列跟哪一列湊成一列;這是為了指認它而取的稱呼,SQL 的標準術語裡沒有這個詞。條件寫進那裡,配對關係就成為查詢結構的一部分;條件留在 WHERE,配對關係就退化成一道篩子。

本篇的查詢跑在共用的範例資料庫上:三位顧客佳穎、宗翰、雅文,而只有佳穎下過兩張訂單(101 與 102,金額 300 與 500)。

這一篇處理的是起手式——寫連接時第一個下筆的地方,而它決定了條件之後能放哪裡。SQL.5 ON 與 WHERE:連接條件與篩選條件各自的職責 處理的是條件寫下之後放哪一邊——那一篇會把配對與成品(關係組完之後得到的那一批列)並排;本篇兩個詞都會用到,而判斷一個條件屬於配對還是成品的完整判定表在那一篇。順序上這一篇在前:先有配對關係,才有條件該放哪裡的問題。

從全部配對縮小的起手式與關聯代數的定義

關聯代數把連接定義成兩個運算的組合:先取笛卡兒積,再用條件選取。所以下面這個寫法是把定義翻成 SQL:

1SELECT 顧客.姓名, 訂單.訂單編號
2FROM 顧客 CROSS JOIN 訂單
3WHERE 訂單.顧客編號 = 顧客.顧客編號;

它讀起來自然是有來由的——每一種配對先擺出來,再挑走不要的。而它是語意層的定義:代數用它說明連接的結果集是什麼,沒有規定寫法要照這個順序展開。引擎也沒有照著做,這一段的計畫與 JOIN ... ON 寫同一個條件逐字相同(SQL.20 關鍵字的宣告與引擎的行為:宣告落空的形態與查證)。

這個起手式在內連接底下一直行得通,因為條件放 ON 與放 WHERE 在內連接底下回同一批列。行得通的經驗會累積,直到問題需要保留配不到的列。

從全部配對縮小與寫進 ON 在外連接處的差別

書店要一份名單:每位顧客與他 300 元以上的訂單,而沒有這種訂單的顧客也要列出來。

從全部配對縮小:

1FROM 顧客 CROSS JOIN 訂單
2WHERE 訂單.顧客編號 = 顧客.顧客編號 AND 訂單.金額 >= 300;
3-- 佳穎 101 / 佳穎 102

宗翰與雅文不在名單上。CROSS JOIN 這個關鍵字沒有外連接的形式,所以要讓宗翰與雅文這種沒有訂單的顧客也留在結果裡(這是外連接做的事,下文叫保護:被保護的表裡配不到對象的列照樣留下,另一張表——下文叫補 NULL 的表——的欄位在那一列補上 NULL),這個起手式本身就得換掉。

把 CROSS JOIN 換成 LEFT JOIN 而條件留在原處:

1FROM 顧客 LEFT JOIN 訂單 ON 訂單.顧客編號 = 顧客.顧客編號
2WHERE 訂單.金額 >= 300;
3-- 佳穎 101 / 佳穎 102

兩人仍然不在。LEFT 保護下來的那些列右邊全是 NULL,而 WHERE 那一步把它們篩掉了(SQL.5 ON 與 WHERE:連接條件與篩選條件各自的職責)。換掉關鍵字補不回那些列,因為要動的是條件的位置。

把配對關係整個寫進 ON:

1FROM 顧客 LEFT JOIN 訂單
2  ON 訂單.顧客編號 = 顧客.顧客編號 AND 訂單.金額 >= 300;
3-- 佳穎 101 / 佳穎 102 / 宗翰 (空)/ 雅文 (空)

三位顧客都在。從全部配對縮小與把配對關係寫進 ON 這兩個起手式,差別在這裡從「寫法偏好」變成「答得出來與答不出來」:從全部配對縮小的起手式把所有條件都放進 WHERE,在成品上篩,而外連接的保護正是在成品那一步被抵銷的。

連接種類的選擇:配不到的列在答案裡有沒有位置

哪兩列該配在一起說完之後,接著要決定的是配不到對象的列怎麼處理。判斷標準是一句話:這些列在答案裡有沒有位置。

有位置就用外連接,而哪一側有位置決定寫 LEFT、RIGHT 還是 FULL;沒有位置就用內連接(JOIN)(外連接那張卡寫三者各保護誰)。「哪些顧客一張單都沒下」這個問題的答案整個在補 NULL 的那幾列上,內連接會讓它無從回答。

還有第三種情形:只問配不配得到,右表的欄位一個都不輸出。那時要的運算是半連接——配起來之後只帶走左邊那一份,右邊只當條件(Semi-join 與 Anti-join)。寫成 JOIN 會讓配得到多列的那些左列重複出現,SQL.8 IN、EXISTS 與 JOIN 寫這三種寫法在列數上的差別,以及選哪一種的判斷標準。

配不到的列要不要保護,這個決定由問題決定,不由現有資料決定。 「翻遍現在的訂單表,每一列都配得到顧客」只證明此刻如此,而查詢要活得比這一批資料久。反過來說,資料保證配得到的時候寫 LEFT 也有代價——它宣告了一個從未發生的情況,下一個讀的人會以為那些列存在(SQL.20 關鍵字的宣告與引擎的行為:宣告落空的形態與查證)。要判斷配對有沒有保證,去看約束,而那條保證要真的生效才算數(SQL.18 外鍵與參照完整性:宣告、生效與查詢得到的保證)。

業務描述對應到查詢的位置:ON、連接種類與 WHERE

「每位顧客與他 300 元以上的訂單,沒有這種訂單的顧客也要列出來」這句話裡有三個決定,各自對應查詢的一個位置。

哪兩列該配在一起。 顧客的那一列與訂單的那一列,靠顧客編號對上,而且只跟 300 元以上的訂單配。這一整句進 ON——「只跟 300 元以上的訂單配」描述的是配對,所以它屬於那裡。

配不到的列要不要。 「沒有這種訂單的顧客也要列出來」說了要,而要保護的是顧客那一側,所以是 LEFT JOIN,左邊放顧客。兩張表的時候這樣判就夠;三張以上時「左邊」是前面幾個連接累積出來的結果,鏈中間放一個 RIGHT 會把前段保護下來的列整批丟掉。累積的判讀,以及同一組資料上四列變一列的實跑,在 SQL.4 鏈式連接的左運算元:累積方式與外連接的保護範圍。

成品裡再篩什麼。 這句話沒有再提條件,所以 WHERE 是空的。業務描述裡沒有針對顧客那一側的限制時,空的 WHERE 是一個合理的結果。

順序反過來走也能檢查已經寫好的查詢:WHERE 裡引用右表欄位的條件,回頭問它描述的是配對,還是成品——多數形態上它描述的是配對,而它待在那裡正在抵銷某個保護。有一個形態剛好相反:WHERE 右表.某欄 IS NULL 挑的正是外連接補上 NULL 的那些列,它描述的是成品,待在 WHERE 是對的位置(SQL.5 ON 與 WHERE:連接條件與篩選條件各自的職責 有完整的判定表,SQL.20 關鍵字的宣告與引擎的行為:宣告落空的形態與查證 用它示範查孤兒訂單)。

只提到被保護的表的條件該放的位置

上一節那句業務描述裡,「哪兩列該配在一起」與「配不到的列要不要」都在問條件與連接種類該怎麼配。而條件提到哪一側,會讓「哪兩列該配在一起」的答案整個不同。換一對表看得清楚。「每張訂單與它的評價,只要三月五日以後下的單」——被保護的是訂單,而「三月五日以後」只提到訂單。條件寫進 ON:

1SELECT 訂單.訂單編號, 訂單.下單日, 評價.星等
2FROM 訂單 LEFT JOIN 評價
3  ON 評價.訂單編號 = 訂單.訂單編號 AND 訂單.下單日 >= '2026-03-05';
4-- 101  2026-03-02  (空)
5-- 102  2026-03-09  (空)

三月二日那張照樣回來,篩選沒有發生——LEFT 的定義是左邊每一列都留著,而 ON 只決定右邊配不配得上,配不上就補 NULL。這個條件改變的是配對:101 本來配得上那筆五星評價,多了這個條件之後整個 ON 對它判假,於是連評價一起丟了。同一個條件改放 WHERE 才回到要的那批列:

1SELECT 訂單.訂單編號, 訂單.下單日, 評價.星等
2FROM 訂單 LEFT JOIN 評價 ON 評價.訂單編號 = 訂單.訂單編號
3WHERE 訂單.下單日 >= '2026-03-05';
4-- 102  2026-03-09  (空)

只提到被保護的表的條件描述的是成品,屬於 WHERE。判斷標準因此要分兩步:先問這個條件提到哪幾張表,只提被保護的表的直接進 WHERE;兩張表都提到、或只提補 NULL 的表的,才問它描述配對還是成品。自連接寫成外連接時也是這樣。自連接指同一張表在一段查詢裡出現兩次、各自扮演一個角色,例如同一張訂單表一次代表較早的訂單、一次代表較晚的訂單;兩次出現裡被保護的那一次,它自己的過濾條件寫進 ON 同樣篩不掉列。配對的兩側是同一張表時,兩次出現要各自有名字才分得開,SQL.7 表的出現與別名:表名的指稱、別名的作用範圍與自連接 用三個引擎的錯誤訊息示範為什麼非取名不可,並把自連接處理的關係分成前後配對、同組比較與層級關係三種形態。放右表的條件另有一組判定,以及它為什麼對左表條件恆為真,在 SQL.5 ON 與 WHERE:連接條件與篩選條件各自的職責。

所以空的 WHERE 不是一個要去追求的形狀。上一節那句業務描述留下空的 WHERE,是因為它確實沒有再提條件;把一個只提被保護的表的條件塞進 ON 去換一個空的 WHERE,換來的是一個篩不掉列、還會丟掉本來成立的配對的條件。