多表連接是 SQL 查詢的核心技能。本文通過具體資料集,圖解 LEFT JOIN (左連接)、RIGHT JOIN (右連接)、INNER JOIN (內連接) 的執行機制、結果差異與適用場景。
CREATE TABLE user_id (id DECIMAL(18));
CREATE TABLE user_profile (id DECIMAL(18), name VARCHAR(255));
-- user_id: 8 筆 (含重複 id=1)
INSERT INTO user_id VALUES (1),(2),(3),(4),(5),(6),(1);
-- user_profile: 7 筆 (含重複 id=5, 且有 id=8 無對應)
INSERT INTO user_profile VALUES
(1,'aa'),(2,'bb'),(3,'cc'),(4,'dd'),(5,'ee'),(5,'EE'),(8,'zz');
SELECT a.id, IFNULL(b.name, 'N/A') AS name
FROM user_id a
LEFT JOIN user_profile b ON a.id = b.id;
+------+------+ | id | name | +------+------+ | 1 | aa | | 2 | bb | | 3 | cc | | 4 | dd | | 5 | ee | | 5 | EE | | 6 | N/A | ← 右表無對應,NULL 補齊 | 1 | aa | ← 左表重複 id=1,各自匹配 +------+------+
不符合以上規則的右表行 (如 id=8) 完全不會出現。
SELECT COUNT(*)
FROM user_id
LEFT JOIN user_profile ON user_id.id = user_profile.id
WHERE user_profile.id IS NULL;
SELECT a.id, IFNULL(b.name, 'N/A') AS name
FROM user_id a
RIGHT JOIN user_profile b ON a.id = b.id;
+------+------+ | id | name | +------+------+ | 1 | aa | | 2 | bb | | 3 | cc | | 4 | dd | | 5 | ee | | 5 | EE | | NULL | zz | ← 左表無 id=8,左側全 NULL +------+------+
左表獨有行 (id=6) 不會出現。
SELECT * FROM user_id a INNER JOIN user_profile b ON a.id = b.id;
-- 等價舊寫法
SELECT * FROM user_id a, user_profile b WHERE a.id = b.id;
+------+------+------+ | id | id | name | +------+------+------+ | 1 | 1 | aa | | 1 | 1 | aa | | 2 | 2 | bb | | 3 | 3 | cc | | 4 | 4 | dd | | 5 | 5 | ee | | 5 | 5 | EE | +------+------+------+
| 維度 | LEFT JOIN | RIGHT JOIN | INNER JOIN |
|---|---|---|---|
| 保留基準 | 左表所有行 | 右表所有行 | 雙方都有的行 |
| 結果行數 | ≥ 左表行數 | ≥ 右表行數 | ≤ 雙表最小行數 |
| NULL 可能 | 右側欄位 | 左側欄位 | 無 |
| 典型場景 | 主表帶從表 (訂單+明細) | 從表反查主表 | 雙方必須關聯 (用戶+訂單) |
| MySQL 支援 | ✅ | ✅ | ✅ |
ON a.id = b.id 兩側欄位均需索引type=ref/eq_ref、rows 預估行數SELECT * FROM user_id a LEFT JOIN user_profile b ON a.id = b.id
UNION
SELECT * FROM user_id a RIGHT JOIN user_profile b ON a.id = b.id;
結果包含所有 9 種組合 (左獨有、右獨有、雙向匹配)。