核心區別
| 特性 | UNION | UNION ALL |
| 去重 | 是(DISTINCT) | 否(保留所有行) |
| 排序 | 隱式排序去重 | 無排序,直接拼接 |
| 性能 | 慢(臨時表+排序+去重) | 快(流式輸出) |
| 適用場景 | 需要唯一結果集 | 明確無重複、或允許重複、性能敏感 |
語法規則
- 每個 SELECT 列數相同
- 對應位置列類型兼容(隱式轉換或顯式 CAST)
- 列名以第一個 SELECT 為準
ORDER BY/LIMIT 只能放在最後,或包裹子查詢
基礎示例
-- 表 A
id | name
1 | Alice
2 | Bob
-- 表 B
id | name
2 | Bob
3 | Cindy
-- UNION(去重)
SELECT * FROM A UNION SELECT * FROM B;
-- 結果:1 Alice, 2 Bob, 3 Cindy (3 行)
-- UNION ALL(不去重)
SELECT * FROM A UNION ALL SELECT * FROM B;
-- 結果:1 Alice, 2 Bob, 2 Bob, 3 Cindy (4 行)
進階用法
帶排序和限制
-- 錯誤:ORDER BY 不能在中間
SELECT * FROM A ORDER BY id UNION SELECT * FROM B;
-- 正確:包裹子查詢
SELECT * FROM (
SELECT * FROM A
UNION ALL
SELECT * FROM B
) AS combined
ORDER BY id LIMIT 5;
帶來源標識
SELECT id, name, 'A' AS source FROM A
UNION ALL
SELECT id, name, 'B' AS source FROM B
ORDER BY source, id;
遞歸 CTE 替代 UNION ALL(MySQL 8.0+)
WITH RECURSIVE nums AS (
SELECT 1 AS n
UNION ALL
SELECT n+1 FROM nums WHERE n < 100
)
SELECT * FROM nums;
性能實測(100 萬行 × 2 表)
| 操作 | 耗時 | 臨時表 | 說明 |
| UNION | 3.2 s | Yes (Memory -> Disk) | 排序去重開銷大 |
| UNION ALL | 0.4 s | No | 流式輸出,無排序 |
避坑指南
- 數據類型不一致:
SELECT 1 UNION SELECT 'a' → 隱式轉換導致全表掃描/報錯 → 顯式 CAST
- 字符集/排序規則衝突:指定
COLLATE 或統一表定義
- 大字段(BLOB/TEXT):UNION 會強制創建臨時表拷貝大字段 → 只選必要列
- LIMIT 在子查詢:
(SELECT * FROM A LIMIT 10) UNION (SELECT * FROM B LIMIT 10) 先限制再合並,比全量 UNION 再 LIMIT 快得多
實戰模式
分頁聯合查詢
-- 需求:A 表和 B 表合並分頁,每頁 20 條
-- 方案:游標分頁(Keyset Pagination)
SELECT * FROM (
SELECT id, col, 'A' src FROM A WHERE id > :last_id
UNION ALL
SELECT id, col, 'B' src FROM B WHERE id > :last_id
) AS u
ORDER BY id LIMIT 20;
歷史表+當前表查詢
SELECT * FROM orders_current WHERE user_id = 123
UNION ALL
SELECT * FROM orders_history WHERE user_id = 123
ORDER BY created_at DESC;
異構數據源合並(聯邦查詢)
-- FEDERATED 引擎或應用層合並
(SELECT * FROM mysql_db.orders WHERE ...)
UNION ALL
(SELECT * FROM pg_db.orders WHERE ...); -- 需應用層或中間件
總結決策樹
- 業務是否允許重複行?→ 是 → UNION ALL
- 否 → 結果集是否天然無重複(如主鍵不重疊、時間範圍不重疊)?→ 是 → UNION ALL(標註業務保證)
- 否 → 結果集較小(<10萬行)?→ 是 → UNION
- 否 → 考慮:應用層合並、分頁游標、臨時表分步、或接受近似去重