《MySQL 入門教程》第 10 篇 數據排序

ORDER BY

默認情況下,SELECT語句不會對返回的結果進行排序,意味着查詢結果的顯示順序是不確定的。如果想要將結果按照某種規則進行排序,例如按照入職先後順序顯示員工的信息,可以使用ORDER BY子句。

10.1 基於單個字段排序

按照單個字段的值進行排序稱爲單列排序。單列排序的語法如下:

SELECT col1, col2, ... 
  FROM table_name
[WHERE conditions]
 ORDER BY col1 [ASC | DESC];

其中,ORDER BY用於指定排序的字段;ASC表示升序排序(Ascending),DESC表示降序排序(Descending),默認值爲升序排序。

例如,以下查詢按照員工的入職先後順序進行排序顯示:

select emp_name, hire_date
from employee
order by hire_date;
 emp_name|hire_date |
---------|----------|
關羽      |2000-01-01|
張飛      |2000-01-01|
劉備      |2000-01-01|
孫尚香    |2002-08-08|
孫丫鬟    |2002-08-08|
趙雲      |2005-12-19|
...

對於升序排序,數字按照從小到大的順序排列,字符按照編碼的順序排列,日期時間按照從早到晚的順序排列;降序排序正好相反。

在上面的查詢結果中,入職日期爲 2000-01-01 的員工有 3 位。那麼他們誰排在前面,誰排在後面呢?答案是不確定。如果要解決這個問題,需要使用多列排序。

10.2 基於多個字段排序

多列排序是指基於多個字段的值排序,多個字段使用逗號進行分隔。多列排序的語法如下:

SELECT col1, col2, ... 
  FROM table_name
[WHERE conditions]
 ORDER BY col1 [ASC | DESC], col2 [ASC | DESC], ...;

執行過程中,先基於第一個字段進行排序;對於第一個字段排序相同的數據,再基於第二個字段進行排序;依此類推。

例如,以下語句查詢行政管理部(dept_id = 1)的員工信息;按照入職先後進行排序,入職日期相同再按照月薪從高到低排序:

select emp_name, sex, hire_date, salary
from employee
where dept_id = 1
order by hire_date asc, salary desc;
 emp_name|sex |hire_date |salary  |
---------|----|----------|--------|
劉備      ||2000-01-01|30000.00|
關羽      ||2000-01-01|26000.00|
張飛      ||2000-01-01|24000.00|

10.3 基於表達式排序

除了使用字段的值進行排序之外,也可以基於表達式的結果進行排序。例如,以下語句按照年度總收入(年薪加獎金)進行排序:

select emp_name, salary, bonus, salary * 12 + ifnull(bonus, 0) as total_income
from employee
order by total_income;
 emp_name|salary  |bonus   |salary * 12 + ifnull(bonus, 0)|
 emp_name|salary  |bonus   |total_income|
---------|--------|--------|------------|
鄧芝      | 4000.00|        |    48000.00|
蔣琬      | 4000.00| 1500.00|    49500.00|
黃權      | 4200.00|        |    50400.00|
龐統      | 4100.00| 2000.00|    51200.00|
糜竺      | 4300.00|        |    51600.00|
孫乾      | 4700.00|        |    56400.00|
...

其中,ifnull(bonus, 0) 函數用於將 bonus 爲空的數據轉換爲 0;該函數將會在後續文章中進行介紹。

另外,在指定排序字段時,除了使用字段名或者表達式之外,也可以使用它們在查詢列表中出現的數字編號順序。上面的示例可以改寫如下:

select emp_name, salary, bonus, salary * 12 + ifnull(bonus, 0) as total_income
from employee
order by 4;

在查詢列表中,total_income 是返回的第 4 個字段;因此該語句也是按照年度總收入從低到高進行排序。

10.4 空值排序

空值(NULL)在 SQL 中表示未知或者缺失的值。如果排序的字段中存在空值時,結果會怎麼樣呢?以下語句按照獎金從高到低進行排序:

select emp_name, bonus
from employee
where dept_id = 3
order by bonus desc;
  emp_name|bonus  |
----------|-------|
孫尚香     |5000.00|
孫丫鬟     |       |

從查詢結果可以看到,空值排在了最後。也就是說,MySQL 認爲空值最小,升序時排在最前,降序時排在最後。

如果想要調整空值的排序位置,可以使用函數(例如 ifnull)將空值轉換爲一個指定的值。例如,以下語句將獎金爲空的數據轉換爲 0:

select emp_name, ifnull(bonus, 0) as bonus
from employee
where dept_id = 3
order by bonus desc;
  emp_name|bonus  |
----------|-------|
孫尚香     |5000.00|
孫丫鬟     |   0.00|

10.5 中文排序

我們可以爲數據庫、表或者字段指定一個字符集(Charset)和排序規則(Collation)。字符集決定了能夠存儲哪些字符,比如 ASCII 字符集只能存儲簡單的英文、數字和一些控制字符;GB2312 字符集可以存儲中文;Unicode 字符集能夠支持世界上的各種語言。

排序規則定義了字符集中字符的排序順序,包括是否區分大小寫,是否區分重音等。對於中文而言,排序方式與英文有所不同;中文通常需要按照拼音、偏旁部首或者筆畫進行排序。

MySQL 8.0 默認使用 utf8mb4 字符編碼,默認的排序規則爲 utf8mb4_0900_ai_ci,對於中文按照偏旁部首進行排序。以下語句按照員工的姓名進行排序:

select emp_name, email
from employee
where dept_id = 5
order by emp_name;
 emp_name|email               |
---------|--------------------|
孫乾      |sunqian@shuguo.net  |
龐統      |pangtong@shuguo.com |
法正      |fazheng@shuguo.com  |
簡雍      |jianyong@shuguo.com |
糜竺      |mizhu@shuguo.com    |
蔣琬      |jiangwan@shuguo.com |
鄧芝      |dengzhi@shuguo.com  |
黃權      |huangquan@shuguo.com|

如果想要按照拼音進行排序,可以指定排序規則。例如:

select emp_name, email
from employee
where dept_id = 5
order by emp_name collate 'utf8mb4_zh_0900_as_cs';
 emp_name|email               |
---------|--------------------|
鄧芝      |dengzhi@shuguo.com  |
法正      |fazheng@shuguo.com  |
黃權      |huangquan@shuguo.com|
簡雍      |jianyong@shuguo.com |
蔣琬      |jiangwan@shuguo.com |
糜竺      |mizhu@shuguo.com    |
龐統      |pangtong@shuguo.com |
孫乾      |sunqian@shuguo.net  |

按照拼音進行排序的另一種方法就是將數據轉換爲其他字符集。例如 gbk:

select emp_name, email
from employee
where dept_id = 5
order by convert(emp_name using gbk);
 emp_name|email               |
---------|--------------------|
鄧芝      |dengzhi@shuguo.com  |
法正      |fazheng@shuguo.com  |
黃權      |huangquan@shuguo.com|
簡雍      |jianyong@shuguo.com |
蔣琬      |jiangwan@shuguo.com |
糜竺      |mizhu@shuguo.com    |
龐統      |pangtong@shuguo.com |
孫乾      |sunqian@shuguo.net  |

CONVERT 是一個函數,用於轉換數據的字符集編碼;這裏是中文 GBK 字符集,默認使用拼音排序。當然,我們也可以在創建數據庫或者表(字段)時指定支持中文排序的排序規則,查詢時就不需要再做任何操作了。

10.6 自定義排序

自定義排序可以按照我們預先定義好的特定順序進行排序,關鍵在於如何定義每個數據的順序。例如,以下查詢通過 field() 函數實現自定義的排序:

select emp_name, field(emp_name, '劉備','關羽','張飛') as num
from employee
where dept_id = 1
order by field(emp_name, '劉備','關羽','張飛');
 emp_name|num|
---------|---|
劉備      |  1|
關羽      |  2|
張飛      |  3|

通過查詢結果可以看出,field(str,str1,str2,str3,…) 函數返回了 str 在後續列表中的下標位置,沒有匹配到數據時返回 0。以上示例實際上相當於將“劉備”編號爲 1,“關羽”編號爲 2,“張飛”編號爲 3。

除了 field() 函數之外,我們還可以通過 CASE 表達式實現類似的轉換邏輯,在後續文章中會介紹 CASE 表達式的作用。

另一種實現自定義排序的方法就是在表中增加一個額外的排序字段,爲數據設置不同的數字,然後通過這個字段進行排序。例如,應用程序中的菜單就可以通過這種方式進行排序顯示。

如果你在學習過程中遇到任何問題,歡迎留言提問,不用客氣!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章