多表查詢是 SQL 進階的核心戰場。本文系統梳理 UNION、各類 JOIN、子查詢的語法差異、適用場景與效能特徵,並提供「如何選擇最佳方案」的決策樹,助你寫出既正確又高效的多表查詢。
UNION 與 UNION ALL:縱向合併結果集
-- UNION:去重(預設),效能較差
SELECT a1 FROM table1 UNION SELECT a1 FROM table2;
-- UNION ALL:保留重複,效能優,業務允許重複時首選
SELECT a1 FROM table1 UNION ALL SELECT a1 FROM table2;
- 欄位數、順序、型別必須相容
ORDER BY、LIMIT 需包在子查詢或最外層
- 適用:同結構分表合併、歷史/現存資料統一查詢
JOIN 家族全景圖
| 類型 | 關鍵字 | 語意 | 典型場景 |
| 內連接 | INNER JOIN / JOIN | 僅返回雙方匹配行 | 主從表關聯、維度表查詢 |
| 左外連接 | LEFT [OUTER] JOIN | 左表全保留,右表無匹配填 NULL | 主表帶明細、報表保留主維度 |
| 右外連接 | RIGHT [OUTER] JOIN | 右表全保留 | 極少用,可改 LEFT JOIN 交換表順序 |
| 交叉連接 | CROSS JOIN / , | 笛卡爾積(無條件) | 生成測試資料、矩陣運算 |
實戰範例:用戶與操作記錄
-- 用戶表
id | name
1 | libk
2 | zyfon
3 | daodao
-- 操作表
user_id | action
1 | jump
1 | kick
2 | run
4 | swim
-- LEFT JOIN:所有用戶 + 操作(無操作顯示 NULL)
SELECT u.id, u.name, a.action
FROM `user` u
LEFT JOIN user_action a ON u.id = a.user_id;
/* 結果:
1 libk jump
1 libk kick
2 zyfon run
3 daodao NULL
※ user_id=4 的 swim 不出現(右表孤兒) */
反向查詢:找出「在左表不在右表」的資料
SELECT u.id, u.name
FROM `user` u
LEFT JOIN user_action a ON u.id = a.user_id
WHERE a.user_id IS NULL; -- 必須用 IS NULL,且判斷欄位為 NOT NULL
/* 結果:3 daodao */
多表連接順序與優化器
- MySQL 優化器自動決定連接順序,但
LEFT JOIN 強制左表優先讀取
- 錯誤順序導致全表掃描:
FROM a,b LEFT JOIN c ON ... WHERE b.key=d.key → 改為 FROM b,a LEFT JOIN c ...
- 強制順序:
STRAIGHT_JOIN 或調整 FROM 子句順序
索引提示:USE INDEX / IGNORE INDEX
SELECT * FROM t1 USE INDEX (idx_a) WHERE a=1 AND b=2;
SELECT * FROM t1 IGNORE INDEX (idx_b) WHERE a=1 AND b=2;
連接條件三寫法對比
| 寫法 | 範例 | 適用場景 |
WHERE | WHERE t1.id = t2.id | 內連接、舊語法 |
ON | LEFT JOIN t2 ON t1.id = t2.id | 外連接、複雜條件、標準語法 |
USING | JOIN t2 USING (id) | 同名欄位連接,簡潔但不支援別名前綴 |
選型決策樹
- 需「縱向合併同結構資料」→
UNION ALL(去重才用 UNION)
- 需「橫向展開關聯資料」→ 進入 JOIN 選型:
- 主表全保留、從表可無 →
LEFT JOIN
- 雙方必須都存在 →
INNER JOIN
- 從表全保留 → 改寫為
LEFT JOIN 交換順序
- 子查詢可改寫為 JOIN 時 → 優先改寫(衍生表物化開銷大)
- 大表關聯 → 確保連接欄位有索引、考慮分區/分表
多表查詢的藝術在於「用最少的掃描換取最精確的結果」。先想業務語意,再選語法,最後用 EXPLAIN 驗證執行計畫,是通往高效 SQL 的唯一捷徑。
copyright ©
萬盛學電腦網 all rights reserved