LEFT JOIN 是多表關聯查詢利器,但當右表匹配行不唯一(1:N)時,結果集行數會超過左表,導致業務邏輯錯誤。本文通過「新聞分類」(1:1)與「會員登入日誌」(1:N)兩個對比案例,演示問題現象與 GROUP BY 子查詢預聚合解決方案。
SELECT a.*, b.col FROM table_a a LEFT JOIN table_b b ON a.id = b.a_id;
SELECT n.id, n.title, c.name AS category_name FROM news n LEFT JOIN news_category c ON n.category_id = c.id;
結果:每條新聞對應唯一分類,無匹配則 category_name 為 NULL ✅



表結構:member(id, username) 1:N member_login_log(uid, logindate)


SELECT m.id, m.username, l.logindate FROM member m LEFT JOIN member_login_log l ON m.id = l.uid;
結果:某會員有 3 條日誌 → 輸出 3 行,會員信息重複 ❌

SELECT m.id, m.username, l.last_login
FROM member m
LEFT JOIN (
SELECT uid, MAX(logindate) AS last_login
FROM member_login_log
GROUP BY uid
) l ON m.id = l.uid;
結果:每會員僅 1 行,last_login 為最近登入時間 ✅

| 業務需求 | 聚合函數 | 適用場景 |
|---|---|---|
| 最近一次 | MAX(date) / MIN(date) | 登入日誌、操作記錄 |
| 統計次數 | COUNT(*) | 訪問頻次、下單次數 |
| 最新狀態 | SUBSTRING_INDEX(GROUP_CONCAT(status ORDER BY time DESC), ',', 1) | 狀態流轉歷史 |
| 金額匯總 | SUM(amount) | 訂單金額、消費總額 |
| 去重列表 | GROUP_CONCAT(DISTINCT col) | 標簽、分類聚合 |
SELECT m.id, m.username, l.logindate
FROM member m
LEFT JOIN (
SELECT uid, logindate,
ROW_NUMBER() OVER (PARTITION BY uid ORDER BY logindate DESC) AS rn
FROM member_login_log
) l ON m.id = l.uid AND l.rn = 1;
優勢:可保留完整行信息(不僅聚合列),MySQL 8.0+ 支援
rows、filtered、Using temporary、Using filesort