核心場景:找出「主表有、從表無」的資料
-- 用戶表 user,資料表 user_profile,關聯鍵 id
-- 查詢 user 中 沒有對應 profile 的用戶
SELECT u.*
FROM user u
LEFT JOIN user_profile up ON u.id = up.id
WHERE up.id IS NULL;
LEFT JOIN 保留左表所有行,右表無匹配則為 NULL。
WHERE up.id IS NULL 過濾出右表無匹配的行。
- 等效
NOT EXISTS 或 NOT IN,但 LEFT JOIN ... IS NULL 在舊版 MySQL 效能通常最佳。
複合條件篩選
-- 查詢 user 等級 > 1 且 profile 金額 > 10000 的用戶
SELECT u.*, up.money
FROM user u
LEFT JOIN user_profile up ON u.id = up.id
WHERE u.level > 1 AND up.money > 10000;
-- 注意:WHERE 條件若包含右表欄位且要求非 NULL,LEFT JOIN 實際退化為 INNER JOIN
-- 若需保留左表所有行並條件過濾右表,條件應寫在 ON 子句
SELECT u.*, up.money
FROM user u
LEFT JOIN user_profile up ON u.id = up.id AND up.money > 10000
WHERE u.level > 1;
ON vs WHERE 差異對照
| 寫法 | 語意 | 結果行數 |
ON u.id=up.id AND up.money>10000 | 關聯時過濾右表,左表全保留 | 左表行數 (右表無匹配顯示 NULL) |
WHERE up.money>10000 | 關聯後過濾,右表 NULL 被排除 | ≤ 左表行數 (等同 INNER JOIN) |
多表 LEFT JOIN 連鎖
SELECT u.name, p.phone, a.address
FROM user u
LEFT JOIN profile p ON u.id = p.user_id
LEFT JOIN address a ON u.id = a.user_id
WHERE u.status = 1;
- 每個
LEFT JOIN 獨立保留左側結果集 (含前面 JOIN 產生的中間結果)。
- 若後續 JOIN 條件在 WHERE,可能意外轉為 INNER JOIN,建議統一寫在 ON。
效能優化建議
- 關聯欄位建立索引:
ALTER TABLE user_profile ADD INDEX idx_user_id (user_id);
- 避免
SELECT *,只取需要欄位減少網路/記憶體。
- 大表分頁查詢改用
延遲關聯:先查主鍵再回表。
EXPLAIN 關注 type=ref、rows 預估。