MySQL全面瓦解9:查詢的排序、分頁相關

翁智華發表於2020-11-13

概述

資料庫中的資料直接呈現出來一般不是我們想要的,所以我們上兩節演示瞭如何對資料進行過濾的方法。除了對資料進行過濾,

我們可能還需要對資料進行排序,比如想從列表中瞭解消費最高的項,就可能需要對金額欄位做降序排序,想看年齡從小到大的分佈情況,就可能需要對user表的age欄位進行升序排序。

也可能需要對資料進行限制,比如我們需要對付款的1~10,11~20,21~30 名的使用者分別贈予不同的禮品,這時候對資料的限制就很有用了。

備註:下面指令碼中[]包含的表示可選,| 分隔符表示可選其一。

資料排序 order by

語法格式如下:

1、需要排序的欄位跟在order by之後;

2、asc 和 desc表示排序的規則,asc:升序,desc:降序,預設為升序 asc;

3、排序可以指定多次欄位,多欄位排序之間用逗號隔開。

4、多欄位排序中,越靠前優先順序越高,下面中cname1優先排序,當cname1等值的時候,cname2開始排序,直至所有欄位都排序完。

1 select cname from tname order by cname1 [asc|desc],cname2 [asc|desc]...;

 

單個欄位排序

舉個例子,在銷售額中通按照交易的訂單進行金額額度降序的方式顯示:

 1 mysql> select * from t_order;
 2 +---------+---------+---------+-------+
 3 | orderid | account | amount  | goods |
 4 +---------+---------+---------+-------+
 5 |       8 | brand   | 52.2    |     2 |
 6 |       9 | hen     | 1752.02 |     7 |
 7 |      10 | helyn   | 88.5    |     4 |
 8 |      11 | sol     | 1007.9  |    11 |
 9 |      12 | diny    | 12      |     1 |
10 |      13 | weng    | 52.2    |     5 |
11 |      14 | sally   | 99.71   |     9 |
12 +---------+---------+---------+-------+
13 7 rows in set
14 
15 mysql> select * from t_order order by amount desc;
16 +---------+---------+---------+-------+
17 | orderid | account | amount  | goods |
18 +---------+---------+---------+-------+
19 |       9 | hen     | 1752.02 |     7 |
20 |      11 | sol     | 1007.9  |    11 |
21 |      14 | sally   | 99.71   |     9 |
22 |      10 | helyn   | 88.5    |     4 |
23 |       8 | brand   | 52.2    |     2 |
24 |      13 | weng    | 52.2    |     5 |
25 |      12 | diny    | 12      |     1 |
26 +---------+---------+---------+-------+
27 7 rows in set

 

多個欄位排序

多個欄位排序用逗號隔開,優先順序從左到右逐次遞減,如下圖,如果金額一致,則按照購買商品數量從多到少排序:

 1 mysql> select * from t_order order by amount desc,goods desc;
 2 +---------+---------+---------+-------+
 3 | orderid | account | amount  | goods |
 4 +---------+---------+---------+-------+
 5 |       9 | hen     | 1752.02 |     7 |
 6 |      11 | sol     | 1007.9  |    11 |
 7 |      14 | sally   | 99.71   |     9 |
 8 |      10 | helyn   | 88.5    |     4 |
 9 |      13 | weng    | 52.2    |     5 |
10 |       8 | brand   | 52.2    |     2 |
11 |      12 | diny    | 12      |     1 |
12 +---------+---------+---------+-------+
13 7 rows in set

按alias排序

按照別名排序或者做條件查詢的目的都是為了簡化程式碼,方便使用,別名可以是英文,也可以是中文:

 1 mysql> select account as ac,amount as am,goods as gd from t_order order by am,gd desc;
 2    
 3 +-------+---------+----+
 4 | ac    | am      | gd |
 5 +-------+---------+----+
 6 | diny  | 12      |  1 |
 7 | weng  | 52.2    |  5 |
 8 | brand | 52.2    |  2 |
 9 | helyn | 88.5    |  4 |
10 | sally | 99.71   |  9 |
11 | sol   | 1007.9  | 11 |
12 | hen   | 1752.02 |  7 |
13 +-------+---------+----+
14 7 rows in set

 

欄位排序中使用函式

下面使用了abs取絕對值函式,所以在 am欄位降序排序中,-99.99 排在 99.71之上。

 1 mysql> select * from t_order;
 2 +---------+---------+---------+-------+
 3 | orderid | account | amount  | goods |
 4 +---------+---------+---------+-------+
 5 |       8 | brand   | 52.2    |     2 |
 6 |       9 | hen     | 1752.02 |     7 |
 7 |      10 | helyn   | 88.5    |     4 |
 8 |      11 | sol     | 1007.9  |    11 |
 9 |      12 | diny    | 12      |     1 |
10 |      13 | weng    | 52.2    |     5 |
11 |      14 | sally   | 99.71   |     9 |
12 |      15 | brand1  | -99.99  |     5 |
13 +---------+---------+---------+-------+
14 8 rows in set
15 
16 mysql> select account as ac,amount as am,goods as gd from t_order order by abs(am) desc;
17    
18 +--------+---------+----+
19 | ac     | am      | gd |
20 +--------+---------+----+
21 | hen    | 1752.02 |  7 |
22 | sol    | 1007.9  | 11 |
23 | brand1 | -99.99  |  5 |
24 | sally  | 99.71   |  9 |
25 | helyn  | 88.5    |  4 |
26 | brand  | 52.2    |  2 |
27 | weng   | 52.2    |  5 |
28 | diny   | 12      |  1 |
29 +--------+---------+----+
30 8 rows in set

與Where條件結合使用

order 在 where 條件之後,根據where已經過濾好的資料再進行排序。下面是過濾出購買金額>80 且 購買數量>5的資料,並且按照價格降序排序。

 1 mysql> select * from t_order;
 2 +---------+---------+---------+-------+
 3 | orderid | account | amount  | goods |
 4 +---------+---------+---------+-------+
 5 |       8 | brand   | 52.2    |     2 |
 6 |       9 | hen     | 1752.02 |     7 |
 7 |      10 | helyn   | 88.5    |     4 |
 8 |      11 | sol     | 1007.9  |    11 |
 9 |      12 | diny    | 12      |     1 |
10 |      13 | weng    | 52.2    |     5 |
11 |      14 | sally   | 99.71   |     9 |
12 |      15 | brand1  | -99.99  |     5 |
13 +---------+---------+---------+-------+
14 8 rows in set
15 
16 mysql> select * from t_order where amount>80 and goods>5 order by amount desc;
17 +---------+---------+---------+-------+
18 | orderid | account | amount  | goods |
19 +---------+---------+---------+-------+
20 |       9 | hen     | 1752.02 |     7 |
21 |      11 | sol     | 1007.9  |    11 |
22 |      14 | sally   | 99.71   |     9 |
23 +---------+---------+---------+-------+

資料limit

很多時候我們過濾出符合要求的資料之後,還需要得到這些資料中的某一個具體區間,比如對付款超過1000的使用者的第1~10,11~20,21~30 名分別贈予不同的禮品,這時候就要使用limit操作了。

limit用來限制select查詢返回的資料,常用於資料排行或者分頁等情況。

語法格式如下:

1 select cname from tname limit [offset,] count;

 

1、offset表示偏移量,就是指跳過的行數,可以省略不寫,預設為0,表示跳過0行,如 limit 8 等同於 limit 0,8。

2、count:跳過偏移量offset之後開始取的資料行數,有count行。

3、limit中offset和count的值不能用表示式。

獲取前n條記錄

如下圖,limit n 和 limit 0,n 是一致的:

 1 mysql> select * from t_order;
 2 +---------+---------+---------+-------+
 3 | orderid | account | amount  | goods |
 4 +---------+---------+---------+-------+
 5 |       8 | brand   | 52.2    |     2 |
 6 |       9 | hen     | 1752.02 |     7 |
 7 |      10 | helyn   | 88.5    |     4 |
 8 |      11 | sol     | 1007.9  |    11 |
 9 |      12 | diny    | 12      |     1 |
10 |      13 | weng    | 52.2    |     5 |
11 |      14 | sally   | 99.71   |     9 |
12 |      15 | brand1  | -99.99  |     5 |
13 +---------+---------+---------+-------+
14 8 rows in set
15 
16 mysql> select * from t_order limit 2
17 ;
18 +---------+---------+---------+-------+
19 | orderid | account | amount  | goods |
20 +---------+---------+---------+-------+
21 |       8 | brand   | 52.2    |     2 |
22 |       9 | hen     | 1752.02 |     7 |
23 +---------+---------+---------+-------+
24 2 rows in set
25 
26 mysql> select * from t_order limit 0,2;
27 +---------+---------+---------+-------+
28 | orderid | account | amount  | goods |
29 +---------+---------+---------+-------+
30 |       8 | brand   | 52.2    |     2 |
31 |       9 | hen     | 1752.02 |     7 |
32 +---------+---------+---------+-------+
33 2 rows in set 

limit限制單條記錄

這邊我們獲取支付金額中最大和最小的的一條記錄。可以先使用 order 條件進行排序,然後limit 第1條記錄即可:

 1  1 mysql> select * from t_order;
 2  2 +---------+---------+---------+-------+
 3  3 | orderid | account | amount  | goods |
 4  4 +---------+---------+---------+-------+
 5  5 |       8 | brand   | 52.2    |     2 |
 6  6 |       9 | hen     | 1752.02 |     7 |
 7  7 |      10 | helyn   | 88.5    |     4 |
 8  8 |      11 | sol     | 1007.9  |    11 |
 9  9 |      12 | diny    | 12      |     1 |
10 10 |      13 | weng    | 52.2    |     5 |
11 11 |      14 | sally   | 99.71   |     9 |
12 12 |      15 | brand1  | -99.99  |     5 |
13 13 +---------+---------+---------+-------+
14 14 8 rows in set
15 15 
16 16 mysql> select * from t_order where amount>0 order by amount desc limit 1;
17 17 +---------+---------+---------+-------+
18 18 | orderid | account | amount  | goods |
19 19 +---------+---------+---------+-------+
20 20 |       9 | hen     | 1752.02 |     7 |
21 21 +---------+---------+---------+-------+
22 22 1 row in set
23 23 
24 24 mysql> select * from t_order where amount>0 order by amount asc limit 1;
25 25 +---------+---------+--------+-------+
26 26 | orderid | account | amount | goods |
27 27 +---------+---------+--------+-------+
28 28 |      12 | diny    | 12     |     1 |
29 29 +---------+---------+--------+-------+
30 30 1 row in set
31  

獲取(m,n)區間記錄

即跳過m條,獲取n條,示例如下,我們跳過兩條,從第三條開始,連取四條的操作:

 1 mysql> select * from t_order order by amount;
 2 +---------+---------+---------+-------+
 3 | orderid | account | amount  | goods |
 4 +---------+---------+---------+-------+
 5 |      15 | brand1  | -99.99  |     5 |
 6 |      12 | diny    | 12      |     1 |
 7 |       8 | brand   | 52.2    |     2 |
 8 |      13 | weng    | 52.2    |     5 |
 9 |      10 | helyn   | 88.5    |     4 |
10 |      14 | sally   | 99.71   |     9 |
11 |      11 | sol     | 1007.9  |    11 |
12 |       9 | hen     | 1752.02 |     7 |
13 +---------+---------+---------+-------+
14 8 rows in set
15 
16 mysql> select * from t_order order by amount limit 2,4;
17 +---------+---------+--------+-------+
18 | orderid | account | amount | goods |
19 +---------+---------+--------+-------+
20 |       8 | brand   | 52.2   |     2 |
21 |      13 | weng    | 52.2   |     5 |
22 |      10 | helyn   | 88.5   |     4 |
23 |      14 | sally   | 99.71  |     9 |
24 +---------+---------+--------+-------+
25 4 rows in set

 

分頁的做法與這個類似,我們程式業務上看到的分頁一般有 pageIndex,pageSize等引數,我們通常的做法是 limit pageIndex*pageSize,pageSize。

這邊假設有31條資料,每頁數量pageSize=10,頁面索引pageIndex預設0,則第一頁就是 limit 0,10,第二頁 limit10,10,第三頁 limit 20,10,第四頁 limit 30,10。

注意點:

1、limit 後面不能使用表示式,只能使用明確的數值,否則會爆出異常,比如 limit 0*10,10,是不對的,這個上面提過了。

2、limit後續的數值只能是正整數和0,也就是說,不能是負數,否則同樣會報錯。

3、排序欄位的值相同情況下,排序後分頁會出現混亂重複的情況。

第3點詳細說明下:假如根據age排序,但是有多個age都是20歲的同學,這時候我們3條記錄一頁,就會出現分頁混亂資料重複。因為年齡相同的人有多個,這是幾個人的排序在每次查詢的時候會有不確定性。

舉個例子,下面的分頁,混亂了:

 1 mysql> select * from user3;
 2 +----+------+-------+
 3 | id | age  | name  |
 4 +----+------+-------+
 5 |  1 |   20 | brand |
 6 |  2 |   22 | sol   |
 7 |  3 |   20 | helen |
 8 |  4 | 19.5 | diny  |
 9 |  6 |   19 | a     |
10 |  7 |   20 | b     |
11 |  8 |   20 | c     |
12 |  9 |   20 | d     |
13 | 10 |   20 | e     |
14 | 11 |   23 | f     |
15 +----+------+-------+
16 10 rows in set
17 
18 mysql> select * from user3 order by age limit 0,3; 
19 +----+------+-------+
20 | id | age  | name  |
21 +----+------+-------+
22 |  6 |   19 | a     |
23 |  4 | 19.5 | diny  |
24 |  3 |   20 | helen |
25 +----+------+-------+
26 3 rows in set
27 
28 mysql> select * from user3 order by age limit 3,3;
29  
30 +----+-----+-------+
31 | id | age | name  |
32 +----+-----+-------+
33 |  3 |  20 | helen |
34 |  7 |  20 | b     |
35 |  8 |  20 | c     |
36 +----+-----+-------+
37 3 rows in set
38 
39 mysql> select * from user3 order by age limit 6,3;
40  
41 +----+-----+-------+
42 | id | age | name  |
43 +----+-----+-------+
44 |  7 |  20 | b     |
45 |  3 |  20 | helen |
46 |  2 |  22 | sol   |
47 +----+-----+-------+
48 3 rows in set 

我們的做法是使用重複值欄位做排序的時候再加個唯一依據(一般可以設主鍵),就不會混亂了。

如下示例,正常了:

 1 mysql> select * from user3;
 2 +----+------+-------+
 3 | id | age  | name  |
 4 +----+------+-------+
 5 |  1 |   20 | brand |
 6 |  2 |   22 | sol   |
 7 |  3 |   20 | helen |
 8 |  4 | 19.5 | diny  |
 9 |  6 |   19 | a     |
10 |  7 |   20 | b     |
11 |  8 |   20 | c     |
12 |  9 |   20 | d     |
13 | 10 |   20 | e     |
14 | 11 |   23 | f     |
15 +----+------+-------+
16 10 rows in set
17 
18 mysql> select * from user3 order by age,id limit 0,3;
19  
20 +----+------+-------+
21 | id | age  | name  |
22 +----+------+-------+
23 |  6 |   19 | a     |
24 |  4 | 19.5 | diny  |
25 |  1 |   20 | brand |
26 +----+------+-------+
27 3 rows in set
28 
29 mysql> select * from user3 order by age,id limit 3,3;
30  
31 +----+-----+-------+
32 | id | age | name  |
33 +----+-----+-------+
34 |  3 |  20 | helen |
35 |  7 |  20 | b     |
36 |  8 |  20 | c     |
37 +----+-----+-------+
38 3 rows in set
39 
40 mysql> select * from user3 order by age,id limit 6,3;
41  
42 +----+-----+------+
43 | id | age | name |
44 +----+-----+------+
45 |  9 |  20 | d    |
46 | 10 |  20 | e    |
47 |  2 |  22 | sol  |
48 +----+-----+------+
49 3 rows in set

上述總結

1、order by cname [asc|desc] 用於對查詢結果排序,asc為升序,desc為降序,可以省略,省略情況下預設為asc。

2、limit用來限制查詢結果返回的行數,包含2個引數(offset,count),offset:表示跳過多少行,count:表示跳過offset行之後取的行數。limit中offset可以省略,預設值為0;limit中offset 和 count都必須大於等於0;limit中offset和count的值不能用表示式。

3、分頁排序時,排序欄位不要有重複值,重複值情況下可能會導致分頁結果混亂,建議在後面加一個主鍵排序。

相關文章