UNION ALL` 不去重、效能極佳,是合併同結構結果集首選。本文聚焦 `UNION ALL` 進階場景:多條件分組合併、外層排序、分頁、巢狀組合。
SELECT col1, col2 FROM table1 WHERE condition1
UNION ALL
SELECT col1, col2 FROM table2 WHERE condition2
ORDER BY col1; -- 排序只能在最外層
-- 表: table (id, username, ytime)
-- 需求: id>20 取前 5 筆升序 + id<20 取前 5 筆降序,再整體按 id 降序
-- ❌ 錯誤:子查詢排序被外層 UNION ALL 破壞
SELECT * FROM (SELECT TOP 5 * FROM table WHERE id > 20 ORDER BY id ASC)
UNION ALL
SELECT TOP 5 * FROM table WHERE id < 20 ORDER BY id DESC;
-- ✅ 正確:外層包裹統一排序
SELECT * FROM (
SELECT TOP 5 * FROM table WHERE id > 20 ORDER BY id ASC
UNION ALL
SELECT TOP 5 * FROM table WHERE id < 20 ORDER BY id DESC
) t ORDER BY id DESC;
關鍵:ORDER BY 必須在**最外層**,子查詢內部排序僅為取 Top-N 服務,最終順序以外層為準。
-- 當前表 + 歷史歸檔表 統一查詢
SELECT id, user_id, amount, created_at FROM orders_2024 WHERE user_id = 100
UNION ALL
SELECT id, user_id, amount, created_at FROM orders_2023 WHERE user_id = 100
UNION ALL
SELECT id, user_id, amount, created_at FROM orders_history WHERE user_id = 100
ORDER BY created_at DESC
LIMIT 20;
SELECT 'mysql' AS source, id, name, email, created_at FROM mysql_users WHERE active = 1
UNION ALL
SELECT 'ldap' AS source, uid AS id, cn AS name, mail AS email, createTimestamp AS created_at
FROM ldap_users WHERE accountStatus = 'active'
UNION ALL
SELECT 'api' AS source, user_id AS id, username AS name, email, registered_at AS created_at
FROM api_users WHERE status = 1
ORDER BY created_at DESC;
-- 方案 1:應用層分頁 (推薦)
-- 第一頁
(SELECT ... FROM table1 LIMIT 20)
UNION ALL
(SELECT ... FROM table2 LIMIT 20)
ORDER BY created_at DESC LIMIT 20;
-- 方案 2:包裹子查詢 + LIMIT
SELECT * FROM (
SELECT * FROM table1
UNION ALL
SELECT * FROM table2
) combined
ORDER BY created_at DESC
LIMIT 20 OFFSET 40;
-- 先按類別合併,再全類別合併
SELECT * FROM (
SELECT * FROM sales_east_2024
UNION ALL
SELECT * FROM sales_west_2024
) regional_2024
UNION ALL
SELECT * FROM (
SELECT * FROM sales_east_2023
UNION ALL
SELECT * FROM sales_west_2023
) regional_2023
ORDER BY sale_date DESC;
| 操作 | 耗時 | 記憶體 | 說明 |
|---|---|---|---|
| UNION (去重) | 2.4 秒 | 180 MB | 建立臨時表排序/雜湊去重 |
| UNION ALL | 0.3 秒 | 12 MB | 流式輸出,無臨時表 |
| UNION ALL + 外層 ORDER BY | 0.8 秒 | 45 MB | 需排序最終結果 |
-- 使用 ROW_NUMBER() 替代 UNION ALL 分組取 Top-N
WITH combined AS (
SELECT *, 'table1' AS src FROM table1
UNION ALL
SELECT *, 'table2' AS src FROM table2
),
ranked AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY src ORDER BY created_at DESC) AS rn
FROM combined
)
SELECT * FROM ranked WHERE rn <= 10 ORDER BY src, created_at DESC;
INT UNION ALL VARCHAR 可能導致精度損失或報錯,用 CAST 統一NULL = NULL 為 UNKNOWN,去重時視為不相等COLLATE 統一UNION ALL,除非業務明確要求去重SELECT * 導致結構變更報錯ORDER BY、LIMITCreated_tmp_tables、Created_tmp_disk_tables 指標