Mysql系列第八講 詳解排序和分頁(order by & limit)及存在的坑

qwer1030274531發表於2020-09-29

排序查詢(order by)

電商中:我們想檢視今天所有成交的訂單,按照交易額從高到低排序,此時我們可以使用資料庫中的排序功能來完成。

排序語法:

select 欄位名 from 表名 order by 欄位1 [asc|desc],欄位2 [asc|desc];1
  • 需要排序的欄位跟在order by之後;

  • asc|desc表示排序的規則,asc:升序,desc:降序,預設為asc;

  • 支援多個欄位進行排序,多欄位排序之間用逗號隔開。

單欄位排序

mysql> create table test2(a int,b varchar(10));Query OK, 0 rows affected (0.01 sec)mysql> insert into test2 values (10,'jack'),(8,'tom'),(5,'ready'),(100,'javacode');Query OK, 4 rows affected (0.00 sec)Records: 4  Duplicates: 0  Warnings: 0mysql> select * from test2;+------+----------+| a    | b        |+------+----------+|   10 | jack     ||    8 | tom      ||    5 | ready    ||  100 | javacode |+------+----------+4 rows in set (0.00 sec)mysql> select * from test2 order by a asc;+------+----------+| a    | b        |+------+----------+|    5 | ready    ||    8 | tom      ||   10 | jack     ||  100 | javacode |+------+----------+4 rows in set (0.00 sec)mysql> select * from test2 order by a desc;+------+----------+| a    | b        |+------+----------+|  100 | javacode ||   10 | jack     ||    8 | tom      ||    5 | ready    |+------+----------+4 rows in set (0.00 sec)mysql> select * from test2 order by a;+------+----------+| a    | b        |+------+----------+|    5 | ready    ||    8 | tom      ||   10 | jack     ||  100 | javacode |+------+----------+4 rows in set (0.00 sec)1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950

多欄位排序

比如學生表,先按學生年齡降序,年齡相同時,再按學號升序,如下:

mysql> create table stu(id int not null comment '學號' primary key,age tinyint not null comment '年齡',name varchar(16) comment '姓名');Query OK, 0 rows affected (0.01 sec)mysql> insert into stu (id,age,name) values (1001,18,'路人甲Java'),(1005,20,'劉德華'),(1003,18,'張學友'),(1004,20,'張國榮'),(1010,19,'梁朝偉');Query OK, 5 rows affected (0.00 sec)Records: 5  Duplicates: 0  Warnings: 0mysql> select * from stu;+------+-----+---------------+| id   | age | name          |+------+-----+---------------+| 1001 |  18 | 路人甲Java    || 1003 |  18 | 張學友        || 1004 |  20 | 張國榮        || 1005 |  20 | 劉德華        || 1010 |  19 | 梁朝偉        |+------+-----+---------------+5 rows in set (0.00 sec)mysql> select * from stu order by age desc,id asc;+------+-----+---------------+| id   | age | name          |+------+-----+---------------+| 1004 |  20 | 張國榮        || 1005 |  20 | 劉德華        || 1010 |  19 | 梁朝偉        || 1001 |  18 | 路人甲Java    || 1003 |  18 | 張學友        |+------+-----+---------------+5 rows in set (0.00 sec)123456789101112131415161718192021222324252627282930

按別名排序

mysql> select * from stu;+------+-----+---------------+| id   | age | name          |+------+-----+---------------+| 1001 |  18 | 路人甲Java    || 1003 |  18 | 張學友        || 1004 |  20 | 張國榮        || 1005 |  20 | 劉德華        || 1010 |  19 | 梁朝偉        |+------+-----+---------------+5 rows in set (0.00 sec)mysql> select age '年齡',id as '學號' from stu order by 年齡 asc,學號 desc;+--------+--------+| 年齡   | 學號   |+--------+--------+|     18 |   1003 ||     18 |   1001 ||     19 |   1010 ||     20 |   1005 ||     20 |   1004 |+--------+--------+12345678910111213141516171819202122

按函式排序

有學生表(id:編號,birth:出生日期,name:姓名),如下:

mysql> drop table if exists student;Query OK, 0 rows affected (0.01 sec)mysql> CREATE TABLE student (
    ->   id int(11) NOT NULL COMMENT '學號',
    ->   birth date NOT NULL COMMENT '出生日期',
    ->   name varchar(16) DEFAULT NULL COMMENT '姓名',
    ->   PRIMARY KEY (id)
    -> );Query OK, 0 rows affected (0.01 sec)mysql> insert into student (id,birth,name) values (1001,'1990-10-10','路人甲Java'),(1005,'1960-03-01','劉德華'),(1003,'1960-08-16','張學友'),(1004,'1968-07-01','張國榮'),(1010,'1962-05-16','梁朝偉');Query OK, 5 rows affected (0.00 sec)Records: 5  Duplicates: 0  Warnings: 0mysql>mysql> SELECT * FROM student;+------+------------+---------------+| id   | birth      | name          |+------+------------+---------------+| 1001 | 1990-10-10 | 路人甲Java    || 1003 | 1960-08-16 | 張學友        || 1004 | 1968-07-01 | 張國榮        || 1005 | 1960-03-01 | 劉德華        || 1010 | 1962-05-16 | 梁朝偉        |+------+------------+---------------+5 rows in set (0.00 sec)123456789101112131415161718192021222324252627

需求:按照出生年份升序、編號升序,查詢出編號、出生日期、出生年份、姓名,2種寫法如下:

mysql> SELECT id 編號,birth 出生日期,year(birth) 出生年份,name 姓名 from student ORDER BY year(birth) asc,id asc;+--------+--------------+--------------+---------------+| 編號   | 出生日期     | 出生年份     | 姓名          |+--------+--------------+--------------+---------------+|   1003 | 1960-08-16   |         1960 | 張學友        ||   1005 | 1960-03-01   |         1960 | 劉德華        ||   1010 | 1962-05-16   |         1962 | 梁朝偉        ||   1004 | 1968-07-01   |         1968 | 張國榮        ||   1001 | 1990-10-10   |         1990 | 路人甲Java    |+--------+--------------+--------------+---------------+5 rows in set (0.00 sec)mysql> SELECT id 編號,birth 出生日期,year(birth) 出生年份,name 姓名 from student ORDER BY 出生年份 asc,id asc;+--------+--------------+--------------+---------------+| 編號   | 出生日期     | 出生年份     | 姓名          |+--------+--------------+--------------+---------------+|   1003 | 1960-08-16   |         1960 | 張學友        ||   1005 | 1960-03-01   |         1960 | 劉德華        ||   1010 | 1962-05-16   |         1962 | 梁朝偉        ||   1004 | 1968-07-01   |         1968 | 張國榮        ||   1001 | 1990-10-10   |         1990 | 路人甲Java    |+--------+--------------+--------------+---------------+5 rows in set (0.00 sec)1234567891011121314151617181920212223

說明:

year函式:屬於日期函式,可以獲取對應日期中的年份。

上面使用了2種方式排序,第一種是在order by中使用了函式,第二種是使用了別名排序。

where之後進行排序

有訂單資料如下:

mysql> drop table if exists t_order;Query OK, 0 rows affected, 1 warning (0.00 sec)mysql> create table t_order(
    ->   id int not null auto_increment comment '訂單編號',
    ->   price decimal(10,2) not null default 0 comment '訂單金額',
    ->   primary key(id)
    -> )comment '訂單表';Query OK, 0 rows affected (0.01 sec)mysql> insert into t_order (price) values (88.95),(100.68),(500),(300),(20.88),(200.5);Query OK, 6 rows affected (0.00 sec)Records: 6  Duplicates: 0  Warnings: 0mysql> select * from t_order;+----+--------+| id | price  |+----+--------+|  1 |  88.95 ||  2 | 100.68 ||  3 | 500.00 ||  4 | 300.00 ||  5 |  20.88 ||  6 | 200.50 |+----+--------+6 rows in set (0.00 sec)1234567891011121314151617181920212223242526

需求:查詢訂單金額>=100的,按照訂單金額降序排序,顯示2列資料,列頭:訂單編號、訂單金額,如下:

mysql> select a.id 訂單編號,a.price 訂單金額 from t_order a where a.price>=100 order by a.price desc;+--------------+--------------+| 訂單編號     | 訂單金額     |+--------------+--------------+|            3 |       500.00 ||            4 |       300.00 ||            6 |       200.50 ||            2 |       100.68 |+--------------+--------------+4 rows in set (0.00 sec)12345678910

limit介紹

limit用來限制select查詢返回的行數,常用於分頁等操作。

語法:

select 列 from 表 limit [offset,] count;1

說明:

  • offset:表示偏移量,通俗點講就是跳過多少行,offset可以省略,預設為0,表示跳過0行;範圍:[0,+∞)。

  • count:跳過offset行之後開始取資料,取count行記錄;範圍:[0,+∞)。

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

下面我們列一些常用的示例來加深理解。

獲取前n行記錄

select 列 from 表 limit 0,n;或者select 列 from 表 limit n;123

示例,獲取訂單的前2條記錄,如下:

mysql> create table t_order(
    ->   id int not null auto_increment comment '訂單編號',
    ->   price decimal(10,2) not null default 0 comment '訂單金額',
    ->   primary key(id)
    -> )comment '訂單表';Query OK, 0 rows affected (0.01 sec)mysql> insert into t_order (price) values (88.95),(100.68),(500),(300),(20.88),(200.5);Query OK, 6 rows affected (0.01 sec)Records: 6  Duplicates: 0  Warnings: 0mysql> select * from t_order;+----+--------+| id | price  |+----+--------+|  1 |  88.95 ||  2 | 100.68 ||  3 | 500.00 ||  4 | 300.00 ||  5 |  20.88 ||  6 | 200.50 |+----+--------+6 rows in set (0.00 sec)mysql> select a.id 訂單編號,a.price 訂單金額 from t_order a limit 2;+--------------+--------------+| 訂單編號     | 訂單金額     |+--------------+--------------+|            1 |        88.95 ||            2 |       100.68 |+--------------+--------------+2 rows in set (0.00 sec)mysql> select a.id 訂單編號,a.price 訂單金額 from t_order a limit 0,2;+--------------+--------------+| 訂單編號     | 訂單金額     |+--------------+--------------+|            1 |        88.95 ||            2 |       100.68 |+--------------+--------------+2 rows in set (0.00 sec)1234567891011121314151617181920212223242526272829303132333435363738394041

獲取最大的一條記錄

我們需要獲取訂單金額最大的一條記錄,可以這麼做:先按照金額降序,然後取第一條記錄,如下:

mysql> select a.id 訂單編號,a.price 訂單金額 from t_order a order by a.price desc;+--------------+--------------+| 訂單編號     | 訂單金額     |+--------------+--------------+|            3 |       500.00 ||            4 |       300.00 ||            6 |       200.50 ||            2 |       100.68 ||            1 |        88.95 ||            5 |        20.88 |+--------------+--------------+6 rows in set (0.00 sec)mysql> select a.id 訂單編號,a.price 訂單金額 from t_order a order by a.price desc limit 1;+--------------+--------------+| 訂單編號     | 訂單金額     |+--------------+--------------+|            3 |       500.00 |+--------------+--------------+1 row in set (0.00 sec)mysql> select a.id 訂單編號,a.price 訂單金額 from t_order a order by a.price desc limit 0,1;+--------------+--------------+| 訂單編號     | 訂單金額     |+--------------+--------------+|            3 |       500.00 |+--------------+--------------+1 row in set (0.00 sec)12345678910111213141516171819202122232425262728

獲取排名第n到m的記錄

我們需要先跳過n-1條記錄,然後取m-n+1條記錄,如下:

select 列 from 表 limit n-1,m-n+1;1

如:我們想獲取訂單金額最高的3到5名的記錄,我們需要跳過2條,然後獲取3條記錄,如下:

mysql> select a.id 訂單編號,a.price 訂單金額 from t_order a order by a.price desc;+--------------+--------------+| 訂單編號     | 訂單金額     |+--------------+--------------+|            3 |       500.00 ||            4 |       300.00 ||            6 |       200.50 ||            2 |       100.68 ||            1 |        88.95 ||            5 |        20.88 |+--------------+--------------+6 rows in set (0.00 sec)mysql> select a.id 訂單編號,a.price 訂單金額 from t_order a order by a.price desc limit 2,3;+--------------+--------------+| 訂單編號     | 訂單金額     |+--------------+--------------+|            6 |       200.50 ||            2 |       100.68 ||            1 |        88.95 |+--------------+--------------+3 rows in set (0.00 sec)12345678910111213141516171819202122

分頁查詢

開發過程中,分頁我們經常使用,分頁一般有2個引數:

page:表示第幾頁,從1開始,範圍[1,+∞)

pageSize:每頁顯示多少條記錄,範圍[1,+∞)

如:page = 2,pageSize = 10,表示獲取第2頁10條資料。

我們使用limit實現分頁,語法如下:

select 列 from 表名 limit (page - 1) * pageSize,pageSize;1

需求:我們按照訂單金額降序,每頁顯示2條,依次獲取所有訂單資料、第1頁、第2頁、第3頁資料,如下:

mysql> select a.id 訂單編號,a.price 訂單金額 from t_order a order by a.price desc;+--------------+--------------+| 訂單編號     | 訂單金額     |+--------------+--------------+|            3 |       500.00 ||            4 |       300.00 ||            6 |       200.50 ||            2 |       100.68 ||            1 |        88.95 ||            5 |        20.88 |+--------------+--------------+6 rows in set (0.00 sec)mysql> select a.id 訂單編號,a.price 訂單金額 from t_order a order by a.price desc limit 0,2;+--------------+--------------+| 訂單編號     | 訂單金額     |+--------------+--------------+|            3 |       500.00 ||            4 |       300.00 |+--------------+--------------+2 rows in set (0.00 sec)mysql> select a.id 訂單編號,a.price 訂單金額 from t_order a order by a.price desc limit 2,2;+--------------+--------------+| 訂單編號     | 訂單金額     |+--------------+--------------+|            6 |       200.50 ||            2 |       100.68 |+--------------+--------------+2 rows in set (0.00 sec)mysql> select a.id 訂單編號,a.price 訂單金額 from t_order a order by a.price desc limit 4,2;+--------------+--------------+| 訂單編號     | 訂單金額     |+--------------+--------------+|            1 |        88.95 ||            5 |        20.88 |+--------------+--------------+2 rows in set (0.00 sec)123456789101112131415161718192021222324252627282930313233343536373839

避免踩坑

limit中不能使用表示式

mysql> select * from t_order where limit 1,4+1;ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'limit 1,4+1' at line 1mysql> select * from t_order where limit 1+0;ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'limit 1+0' at line 1mysql>12345

結論:limit後面只能夠跟明確的數字。

limit後面的2個數字不能為負數

mysql> select * from t_order where limit -1;ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'limit -1' at line 1mysql> select * from t_order where limit 0,-1;ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'limit 0,-1' at line 1mysql> select * from t_order where limit -1,-1;ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'limit -1,-1' at line 1123456

排序分頁存在的坑

準備資料:

mysql> insert into test1 (b) values (1),(2),(3),(4),(2),(2),(2),(2);Query OK, 8 rows affected (0.01 sec)Records: 8  Duplicates: 0  Warnings: 0mysql> select * from test1;+---+---+| a | b |+---+---+| 1 | 1 || 2 | 2 || 3 | 3 || 4 | 4 || 5 | 2 || 6 | 2 || 7 | 2 || 8 | 2 |+---+---+8 rows in set (0.00 sec)mysql> select * from test1 order by b asc;+---+---+| a | b |+---+---+| 1 | 1 || 2 | 2 || 5 | 2 || 6 | 2 || 7 | 2 || 8 | 2 || 3 | 3 || 4 | 4 |+---+---+8 rows in set (0.00 sec)123456789101112131415161718192021222324252627282930313233

下面我們按照b升序,每頁2條資料,來獲取資料。

下面的sql依次為第1頁、第2頁、第3頁、第4頁、第5頁的資料,如下:

mysql> select * from test1 order by b asc limit 0,2;+---+---+| a | b |+---+---+| 1 | 1 || 2 | 2 |+---+---+2 rows in set (0.00 sec)mysql> select * from test1 order by b asc limit 2,2;+---+---+| a | b |+---+---+| 8 | 2 || 6 | 2 |+---+---+2 rows in set (0.00 sec)mysql> select * from test1 order by b asc limit 4,2;+---+---+| a | b |+---+---+| 6 | 2 || 7 | 2 |+---+---+2 rows in set (0.00 sec)mysql> select * from test1 order by b asc limit 6,2;+---+---+| a | b |+---+---+| 3 | 3 || 4 | 4 |+---+---+2 rows in set (0.00 sec)mysql> select * from test1 order by b asc limit 7,2;+---+---+| a | b |+---+---+| 4 | 4 |+---+---+1 row in set (0.00 sec)12345678910111213141516171819202122232425262728293031323334353637383940414243

上面有2個問題:

問題1:看一下第2個sql和第3個sql,分別是第2頁和第3頁的資料,結果出現了相同的資料,是不是懵逼了。

問題2:整個表只有8條記錄,怎麼會出現第5頁的資料呢,又懵逼了。

我們來分析一下上面的原因:主要是b欄位存在相同的值,當排序過程中存在相同的值時,沒有其他排序規則時,mysql懵逼了,不知道怎麼排序了。

就像我們上學站隊一樣,按照身高排序,那身高一樣的時候如何排序呢?身高一樣的就亂排了。

建議:排序中存在相同的值時,需要再指定一個排序規則,透過這種排序規則不存在二義性,比如上面可以再加上a降序,如下:

mysql> select * from test1 order by b asc,a desc;+---+---+| a | b |+---+---+| 1 | 1 || 8 | 2 || 7 | 2 || 6 | 2 || 5 | 2 || 2 | 2 || 3 | 3 || 4 | 4 |+---+---+8 rows in set (0.00 sec)mysql> select * from test1 order by b asc,a desc limit 0,2;+---+---+| a | b |+---+---+| 1 | 1 || 8 | 2 |+---+---+2 rows in set (0.00 sec)mysql> select * from test1 order by b asc,a desc limit 2,2;+---+---+| a | b |+---+---+| 7 | 2 || 6 | 2 |+---+---+2 rows in set (0.00 sec)mysql> select * from test1 order by b asc,a desc limit 4,2;+---+---+| a | b |+---+---+| 5 | 2 || 2 | 2 |+---+---+2 rows in set (0.00 sec)mysql> select * from test1 order by b asc,a desc limit 6,2;+---+---+| a | b |+---+---+| 3 | 3 || 4 | 4 |+---+---+2 rows in set (0.00 sec)mysql> select * from test1 order by b asc,a desc limit 8,2;Empty set (0.00 sec)1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253

看上面的結果,分頁資料都正常了,第5頁也沒有資料了。

總結

order by … [asc|desc]用於對查詢結果排序,asc:升序,desc:降序,asc|desc可以省略,預設為asc

limit用來限制查詢結果返回的行數,有2個引數(offset,count),offset:表示跳過多少行,count:表示跳過offset行之後取count行

limit中offset可以省略,預設值為0

limit中offset 和 count都必須大於等於0

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

分頁排序時,排序不要有二義性,二義性情況下可能會導致分頁結果亂序,可以在後面追加一個主鍵排序


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/30239065/viewspace-2725056/,如需轉載,請註明出處,否則將追究法律責任。

相關文章