ORDER BY 是 SQL 中唯一能保證結果集順序的子句。本文系統梳理升/降序、多列排序、表達式/別名/位置排序、非選擇列排序等核心用法,並給出索引優化建議。
SELECT col1, col2 FROM table_name ORDER BY col1 [ASC|DESC], col2 [ASC|DESC] ...
ASC (默認):升序,數值小→大,字母 A→Z,日期早→晚。DESC:降序,反之。ORDER BY col IS NULL, col 顯式控制。SELECT name, salary FROM employees ORDER BY hire_date DESC;
只要列在表中存在即可,不必出現在 SELECT 列表。注意:配合 DISTINCT 或 GROUP BY 時,排序列必須出現在選擇列表/分組列中 (標準 SQL 要求)。
SELECT price, quantity, price*quantity AS total FROM orders ORDER BY price*quantity DESC; -- 或 ORDER BY total DESC (別名)
支持算術運算、函數調用、CASE 表達式等。性能提示:表達式無法直接利用索引,考慮生成列 (Generated Column) 或函數索引 (MySQL 8.0+)。
SELECT CONCAT(first_name, ' ', last_name) AS full_name FROM users ORDER BY full_name;
邏輯執行順序中 SELECT (含別名定義) 先於 ORDER BY,故可用。不可用於 WHERE/GROUP BY/HAVING。
SELECT dept_id, emp_id, salary FROM employees ORDER BY 1 ASC, 2 ASC; -- 先按第1列 dept_id,再按第2列 emp_id
優點:簡潔,UNION 查詢各子查詢列名不一致時必須用位置。缺點:可讀性差,維護易出錯 (增減列導致位置偏移)。建議僅用於臨時調試或 UNION 場景。
SELECT * FROM employees ORDER BY department_id ASC, salary DESC, hire_date ASC;
規則:先按第一列排序;第一列值相同的行,按第二列排序;以此類推。等同於複合索引 (department_id, salary DESC, hire_date) 的順序掃描。
SELECT name FROM users ORDER BY name COLLATE utf8mb4_bin; -- 二進制區分大小寫 SELECT name FROM users ORDER BY name COLLATE utf8mb4_general_ci; -- 不區分大小寫
解決字符集/校對規則導致的排序不符預期問題。
| 場景 | 能否用索引 | 優化建議 |
|---|---|---|
| ORDER BY 索引列,方向一致 | ✅ Index Scan | 建立對應方向索引 |
| ORDER BY 索引列,方向混合 | ✅ (MySQL 8.0+) | 建立混合方向索引 INDEX (a ASC, b DESC) |
| WHERE + ORDER BY 同索引列 | ✅ 最優 | 複合索引 (where_col, order_col) |
| ORDER BY 非索引列 / 表達式 | ❌ Filesort | 增加索引、生成列、或接受 Filesort (小結果集可忍受) |
| JOIN 查詢 ORDER BY 驅動表列 | ✅ 可能 | 驅動表排序列有索引,優化器可用 Index Scan 避免臨時表 |
| LIMIT 小值 + ORDER BY | ✅ 優先隊列 | MySQL 使用 Priority Queue 優化,極快 |
Using filesort:需額外排序操作 (內存/磁盤臨時表),性能開銷大。Using index:覆蓋索引,無需回表。Using temporary; Using filesort:同時用臨時表和排序,典型於 GROUP BY + ORDER BY 列不一致。ORDER BY RAND() 隨機抽樣 → 全表掃描 + 臨時表排序,極慢。替代方案:WHERE id >= (SELECT FLOOR(RAND() * (MAX(id)-MIN(id)+1)) + MIN(id)) LIMIT 1。LIMIT 1000000, 20 + ORDER BY → 掃描 100萬行丟棄。優化:WHERE id > last_seen_id ORDER BY id LIMIT 20 (游標分頁)。