LEFT JOIN 保留左表所有行,右表無匹配則回傳 NULL,是「主表帶從表」查詢的核心工具。
SELECT select_list
FROM table1
LEFT JOIN table2 ON table1.col = table2.col
LEFT JOIN table3 ON table1.col = table3.col
...;
資料模型:
article (aid, title, content, uid, tid)user (uid, username, email)type (tid, typename)查詢:文章標題 + 作者名 + 分類名
SELECT
a.aid,
a.title,
u.username AS author,
t.typename AS category
FROM article a
LEFT JOIN user u ON a.uid = u.uid
LEFT JOIN type t ON a.tid = t.tid;
-- 標準多表 JOIN (推薦,可讀性佳)
FROM t1
LEFT JOIN t2 ON t2.a = t1.a
LEFT JOIN t3 ON t3.b = t1.b
LEFT JOIN t4 ON t4.c = t1.c
-- 簡寫語法 (MySQL 支援,但較難維護)
FROM t1 LEFT JOIN (t2, t3, t4)
ON (t2.a=t1.a AND t3.b=t1.b AND t4.c=t1.c)
| 優化點 | 做法 | 原理 |
|---|---|---|
| 驅動表選擇 | 小表驅動大表,左表應為結果集較小者 | 減少外層迴圈次數 |
| ON 條件索引 | 被 JOIN 表的關聯欄位**必須建立索引** | 避免全表掃描,使用 ref/eq_ref 存取類型 |
| WHERE 過濾位置 | 左表條件放 WHERE,右表條件放 ON | WHERE 會將 LEFT JOIN 退化為 INNER JOIN |
| 只選需要欄位 | 避免 SELECT *,減少網路傳輸與臨時表大小 | 覆蓋索引機會增加 |
| 避免子查詢 | 衍生表改寫為 JOIN 或 CTE (8.0+) | 減少物化臨時表開銷 |
-- ❌ 錯誤:右表條件放 WHERE,NULL 被過濾 → 變成 INNER JOIN
SELECT * FROM article a
LEFT JOIN user u ON a.uid = u.uid
WHERE u.status = 1; -- u.status 為 NULL 的行被排除
-- ✅ 正確:右表條件放 ON
SELECT * FROM article a
LEFT JOIN user u ON a.uid = u.uid AND u.status = 1;
EXPLAIN SELECT ... FROM article a LEFT JOIN user u ON a.uid=u.uid;
-- 理想輸出:
-- table: a, type: ALL (或 ref), rows: 100
-- table: u, type: eq_ref, rows: 1, Extra: Using index
最佳實務: 明確區分「主表 (左)」與「從表 (右)」,ON 條件建索引,右表過濾條件寫在 ON 而非 WHERE,必要時用 STRAIGHT_JOIN 強制連接順序。