默認情況下,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 表達式的作用。
另一種實現自定義排序的方法就是在表中增加一個額外的排序字段,爲數據設置不同的數字,然後通過這個字段進行排序。例如,應用程序中的菜單就可以通過這種方式進行排序顯示。
如果你在學習過程中遇到任何問題,歡迎留言提問,不用客氣!