MySQL 課程 – SELECT 查詢語法
主題:ORDER BY 排序
簡介
在日常的資料查詢工作中,排序是最常見也是最重要的需求之一。無論是顯示商品清單、列出使用者排行榜,或是產生報表,正確的排序都直接影響使用者的閱讀體驗與決策依據。
MySQL 提供的 ORDER BY 子句讓我們可以依照一或多個欄位,以升冪 (ASC) 或降冪 (DESC) 的方式排列結果。雖然語法看似簡單,但在實務上卻常伴隨著效能、字元編碼、NULL 處理等隱藏的陷阱。掌握 ORDER BY 的核心概念與最佳實踐,能讓你的查詢既正確又高效。
本篇將從概念說明、實作範例、常見問題與最佳做法,逐步帶你深入了解 ORDER BY,適合剛入門的初學者,也能為有一定基礎的開發者提供進階的觀點。
核心概念
1. 基本語法
SELECT 欄位1, 欄位2, ...
FROM 資料表
WHERE 條件
ORDER BY 排序欄位1 [ASC|DESC], 排序欄位2 [ASC|DESC], ...;
ASC為預設值,代表 升冪(由小到大、由 A 到 Z)。DESC代表 降冪(由大到小、由 Z 到 A)。- 可以同時指定多個欄位,MySQL 會依序比較,第一個欄位相同時才會比較第二個,以此類推。
小技巧:若只想排序卻不需要返回排序欄位本身,可在
SELECT中省略該欄位,只在ORDER BY中保留。
2. 排序依據的資料型別
| 資料型別 | 排序方式 | 注意事項 |
|---|---|---|
數值 (INT, DECIMAL, …) |
直接比較大小 | 無需額外處理 |
日期/時間 (DATE, DATETIME, TIMESTAMP) |
依時間先後排序 | 時區設定會影響結果 |
字串 (CHAR, VARCHAR, TEXT) |
依字元編碼排序 | 受 collation(字元集排序規則)影響,中文排序常用 utf8mb4_unicode_ci |
| ENUM / SET | 按定義順序排序 | 仍屬於字串類型,建議使用 FIELD() 取得自訂順序 |
3. NULL 的排序行為
- 預設:
NULL被視為 最小值,在ASC時排在最前、在DESC時排在最後。 - 若想改變此行為,可使用
ORDER BY ... IS NULL或COALESCE()來自訂排序。
-- 把 NULL 放到最後(升冪)
SELECT name, score
FROM students
ORDER BY score IS NULL, score ASC;
4. 使用索引提升排序效能
- 若
ORDER BY的欄位與WHERE條件中的欄位相同,且符合 索引的左前綴,MySQL 可以直接利用索引返回已排序的資料,省去額外的排序(filesort)步驟。 - 複合索引(
INDEX(col1, col2))能同時支援ORDER BY col1, col2。 - 避免 在
ORDER BY前使用不必要的SELECT *或JOIN產生大量中間結果,會迫使 MySQL 進行磁碟排序。
5. 進階排序技巧
| 技巧 | 語法 | 說明 |
|---|---|---|
| 自訂排序順序 | ORDER BY FIELD(col, 'A','B','C') |
依指定的列舉值排序,常用於狀態欄位 |
| 多欄位混合升降 | ORDER BY col1 ASC, col2 DESC |
第一欄升冪、第二欄降冪 |
| 排序計算結果 | ORDER BY (price * quantity) DESC |
直接對表達式排序 |
| 字串長度排序 | ORDER BY CHAR_LENGTH(name) ASC |
依字串長度排序 |
| 隨機排序 | ORDER BY RAND() |
常用於抽樣顯示,注意效能 |
程式碼範例
以下示範 5 個實務上常見的 ORDER BY 用法,均以 MySQL 8.0+ 為基礎,並加入完整註解說明。
範例 1:基本升冪與降冪排序
-- 依商品價格由低到高排列
SELECT product_id, product_name, price
FROM products
ORDER BY price ASC; -- ASC 可省略
-- 依庫存數量由高到低排列
SELECT product_id, product_name, stock
FROM products
ORDER BY stock DESC; -- 降冪
範例 2:多欄位混合排序
-- 先依類別 (category_id) 升冪,類別相同時依上架日期降冪
SELECT product_id, product_name, category_id, created_at
FROM products
ORDER BY category_id ASC, created_at DESC;
範例 3:自訂狀態排序(FIELD)
-- 訂單狀態的自訂順序:'pending' > 'processing' > 'shipped' > 'completed'
SELECT order_id, status, order_date
FROM orders
ORDER BY FIELD(status,
'pending',
'processing',
'shipped',
'completed') ASC;
範例 4:將 NULL 放最後(升冪)
-- 顯示會員的最後登入時間,NULL 表示從未登入,放到最底部
SELECT member_id, last_login
FROM members
ORDER BY last_login IS NULL, last_login ASC;
範例 5:使用索引加速排序
-- 假設已有複合索引 (category_id, price)
CREATE INDEX idx_cat_price ON products (category_id, price);
-- 依類別與價格排序,MySQL 可直接走索引,不會產生 filesort
SELECT product_id, product_name, category_id, price
FROM products
WHERE category_id IN (1,2,3)
ORDER BY category_id ASC, price DESC;
備註:在開發環境中可使用
EXPLAIN觀察是否真的走索引,若出現Using filesort表示仍需額外排序,可能是因為索引不符合排序需求或有額外的SELECT、JOIN影響。
常見陷阱與最佳實踐
| 陷阱 | 說明 | 解決方案 |
|---|---|---|
| 忘記指定方向 | ORDER BY col 預設升冪,若實際需求是降冪,結果會相反。 |
明確寫 ASC 或 DESC,降低誤解。 |
| 字元編碼不一致 | utf8 vs utf8mb4、不同 collation 會導致排序結果不符合預期(特別是中文)。 |
統一資料庫與欄位的 CHARACTER SET 與 COLLATE,如 utf8mb4_unicode_ci。 |
| 大量資料排序未使用索引 | 大表直接 ORDER BY 會觸發磁碟排序,效能嚴重下降。 |
建立適當的索引、限制返回列數 (LIMIT)、或先篩選 (WHERE) 再排序。 |
使用 SELECT * 搭配 ORDER BY |
不必要的欄位會增加 I/O,且可能破壞索引排序的使用。 | 只挑選需要的欄位,減少資料量。 |
ORDER BY RAND() |
隨機排序在大資料集上會把整表載入記憶體,極度耗資源。 | 若只需要抽樣,可使用 LIMIT 搭配子查詢或 TABLESAMPLE(MySQL 8.0+)等方法。 |
| NULL 排序不符合需求 | 預設把 NULL 視為最小值,導致意外的排序結果。 | 使用 IS NULL、COALESCE() 或自訂排序式處理。 |
最佳實踐清單
- 明確寫出方向:
ORDER BY col DESC而非省略。 - 盡量使用索引:檢查
EXPLAIN,確保不出現Using filesort。 - 限制結果筆數:配合
LIMIT,尤其在分頁查詢時。 - 統一 Collation:中文排序建議使用
utf8mb4_unicode_ci或utf8mb4_0900_ai_ci。 - 避免在大表直接使用
ORDER BY RAND(),改用其他抽樣技巧。 - 考慮 NULL 處理:根據需求自行決定 NULL 的位置。
- 分頁時使用穩定排序:加入唯一鍵(如
id)作為最後的排序欄位,避免同分頁間資料重複或遺漏。
實際應用場景
| 場景 | 需求 | ORDER BY 實作 |
|---|---|---|
| 商品列表 | 按價格由低到高、同價位依上架時間最新排序 | ORDER BY price ASC, created_at DESC |
| 會員排行榜 | 依積分降冪,積分相同時依註冊日期升冪 | ORDER BY points DESC, register_date ASC |
| 訂單管理系統 | 先顯示「未處理」狀態,再顯示「已處理」;每個狀態內依下單時間最新排序 | ORDER BY FIELD(status, 'pending','processing','shipped','completed'), order_date DESC |
| 報表匯出 | 依部門、年度、薪資降冪產出 Excel | ORDER BY department ASC, year ASC, salary DESC |
| 分頁搜尋 | 大量資料的分頁,需要穩定且可預測的排序 | ORDER BY created_at DESC, id DESC LIMIT 20 OFFSET 0 |
這些案例展示了 排序 在不同業務需求中的核心角色,透過正確的 ORDER BY 設計,不僅提升使用者體驗,也能大幅降低資料庫負載。
總結
ORDER BY 是 MySQL 中最常用、也是最具影響力的子句之一。掌握以下要點,即可寫出 正確、可讀、效能佳 的排序查詢:
- 明確指定排序方向(ASC / DESC)。
- 了解資料型別與 collation,避免字元排序錯誤。
- 妥善處理 NULL,確保結果符合業務邏輯。
- 善用索引,減少
filesort帶來的 I/O 壓力。 - 遵循最佳實踐(限制欄位、分頁、避免 RAND() 等),讓查詢在大資料量下仍保持高效。
透過本篇的概念說明、實作範例與最佳實踐,你已具備在日常開發與資料分析中運用 ORDER BY 的完整能力。未來只要面對更複雜的排序需求,記得回顧本文的「多欄位混合排序」與「自訂排序」技巧,配合適當的索引與測試,即可輕鬆解決。祝你在 MySQL 的世界裡查詢順手、效能滿分!