核心概念:集合論視角
- INNER JOIN = 交集 (A ∩ B) — 僅返回雙邊匹配行。
- LEFT JOIN = 左集合全量 + 右匹配 (A ∪ (A ∩ B)) — 保左表所有行,右無匹配填 NULL。
- RIGHT JOIN = 右集合全量 + 左匹配 — 保右表所有行,左無匹配填 NULL。
最小數據集演示
表 A
表 B
三種 JOIN 結果對比
1. INNER JOIN
SELECT * FROM a INNER JOIN b ON a.aid = b.bid;
| aid | adate | bid | bdate |
| 1 | a1 | 1 | b1 |
| 2 | a2 | 2 | b2 |
僅返回 aid=bid=1,2 的匹配行。
2. LEFT JOIN
SELECT * FROM a LEFT JOIN b ON a.aid = b.bid;
| aid | adate | bid | bdate |
| 1 | a1 | 1 | b1 |
| 2 | a2 | 2 | b2 |
| 3 | a3 | NULL | NULL |
左表 aid=3 無匹配,右側填 NULL。
3. RIGHT JOIN
SELECT * FROM a RIGHT JOIN b ON a.aid = b.bid;
| aid | adate | bid | bdate |
| 1 | a1 | 1 | b1 |
| 2 | a2 | 2 | b2 |
| NULL | NULL | 4 | b4 |
右表 bid=4 無匹配,左側填 NULL。
實戰場景:用戶與訂單
表結構
Persons (Id_P, LastName, FirstName, Address, City)
Orders (Id_O, OrderNo, Id_P)
需求:列出所有人及其訂單 (無訂單也顯示)
SELECT Persons.LastName, Persons.FirstName, Orders.OrderNo
FROM Persons
LEFT JOIN Orders ON Persons.Id_P = Orders.Id_P
ORDER BY Persons.LastName;
結果
| LastName | FirstName | OrderNo |
| Adams | John | 22456 |
| Adams | John | 24562 |
| Carter | Thomas | 77895 |
| Carter | Thomas | 44678 |
| Bush | George | NULL |
Bush George 無訂單,OrderNo 顯示 NULL,完美符合「保左表」語義。
進階:多表鏈式 JOIN
SELECT u.name, o.order_no, p.product_name
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
LEFT JOIN order_items oi ON o.id = oi.order_id
LEFT JOIN products p ON oi.product_id = p.id
WHERE u.status = 1;
- 每個 LEFT JOIN 獨立保左表 (上一步結果集)。
- WHERE 條件放在最後,**右表列不得出現在 WHERE** (除非 IS NULL 判斷),否則退化為 INNER JOIN。
性能優化鐵律
- 小表驅動大表:左表盡量小 (過濾後),右表有索引。
- JOIN 列類型一致:INT 關聯 INT,VARCHAR 關聯 VARCHAR,長度一致,否則隱式轉換導致索引失效。
- 優先 INNER JOIN:語義允許時用 INNER JOIN,優化器選擇更多 (可交換表順序)。
- 避免 SELECT *:僅查詢所需列,減少網絡/內存/臨時表開銷。
- EXPLAIN 關鍵列:
type=ref/eq_ref (索引查找),Extra=Using where; Using index (覆蓋索引)。
總結
JOIN 本質是集合運算。牢記「LEFT 保左、RIGHT 保右、INNER 取交」,結合「右表條件寫 ON、WHERE 只過左表」原則,即可寫出正確、高效的多表查詢。實戰中 90% 場景用 INNER + LEFT 組合即可覆蓋,RIGHT JOIN 可通過交換表順序轉為 LEFT JOIN,統一代碼風格。