UNION 預設合併結果集並去重,ORDER BY 排序最終輸出。兩者結合時,MySQL 對語法位置有嚴格限制:無括號時只允許一個 ORDER BY 置於末尾;若需分別排序再合併,必須用子查詢包裹。本文以實例拆解三種正確寫法與常見誤區。
+----+----------+-------+ | id | username | score | +----+----------+-------+ | 1 | lily | 90 | | 2 | lucy | 85 | | 3 | lily | 80 | | 4 | mike | 95 | | 5 | mary | 88 | +----+----------+-------+

SELECT * FROM t1 WHERE username LIKE 'l%' ORDER BY score ASC
UNION
SELECT * FROM t1 WHERE username LIKE '%m%' ORDER BY score ASC;
-- ERROR 1221 (HY000): Incorrect usage of UNION and ORDER BY
原因:MySQL 解析器無法判斷兩個 ORDER BY 歸屬於哪個 SELECT,且 UNION 會重新排序去重,前置排序無意義。
SELECT * FROM t1 WHERE username LIKE 'l%'
UNION
SELECT * FROM t1 WHERE username LIKE '%m%' ORDER BY score ASC;
語義:先 UNION 去重合併,再對最終結果集按 score 升序。最常用、效能最好(可利用索引排序)。
SELECT * FROM (
SELECT * FROM t1 WHERE username LIKE 'l%' ORDER BY score ASC
) t3
UNION
SELECT * FROM (
SELECT * FROM t1 WHERE username LIKE '%m%' ORDER BY score ASC
) t4;
關鍵: 每個 SELECT 必須包在括號內並給別名(t3, t4),內部 ORDER BY 才生效。但注意:UNION 仍會去重並重新排序,內部排序最終不一定保留(除非加 LIMIT)。
SELECT * FROM (
SELECT * FROM t1 WHERE username LIKE 'l%' ORDER BY score ASC
) t3
UNION ALL
SELECT * FROM (
SELECT * FROM t1 WHERE username LIKE '%m%' ORDER BY score ASC
) t4;
若業務需「第一組升序、第二組升序、兩組依序拼接」,只能用 UNION ALL 並確保無重複行,或在外層再包一層:
SELECT * FROM (
SELECT 1 AS grp, * FROM t1 WHERE username LIKE 'l%' ORDER BY score ASC
UNION ALL
SELECT 2 AS grp, * FROM t1 WHERE username LIKE '%m%' ORDER BY score ASC
) t ORDER BY grp, score;
| 特性 | UNION | UNION ALL |
|---|---|---|
| 去重 | 是(排序+唯一) | 否 |
| 效能 | 較慢(臨時表+排序) | 快(直接流式輸出) |
| 適用場景 | 業務要求結果唯一 | 已知無重複、或允許重複、效能優先 |
ORDER BY **只能出現在最後一個 SELECT 之後**。(SELECT ... ORDER BY ...) alias 子查詢包裹。ORDER BY 若無 LIMIT,MySQL 優化器可能忽略它(視版本而定),務必實測驗證。UNION ALL,應用層去重或加外層 DISTINCT。SELECT 欄位數、順序、類型必須一致,否則隱式轉換導致錯誤或錯誤結果。掌握 UNION 與 ORDER BY 的語法邊界,能寫出既正確又高效的合併查詢,避免「明明加了排序卻不生效」的困擾。