Mysql排序和分頁(order by&limit)及存在的坑
排序查詢(order by)
電商中:我們想查看今天所有成交的訂單,按照交易額從高到低排序,此時(shí)我們可以使用數(shù)據(jù)庫中的排序功能來完成。
排序語法:
select 字段名 from 表名 order by 字段1 [asc|desc],字段2 [asc|desc];
- 需要排序的字段跟在order by之后;
- asc|desc表示排序的規(guī)則,asc:升序,desc:降序,默認(rèn)為asc;
- 支持多個(gè)字段進(jìn)行排序,多字段排序之間用逗號隔開。
單字段排序
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: 0 mysql> 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)
多字段排序
比如學(xué)生表,先按學(xué)生年齡降序,年齡相同時(shí),再按學(xué)號升序,如下:
mysql> create table stu(id int not null comment '學(xué)號' 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,'張學(xué)友'),(1004,20,'張國榮'),(1010,19,'梁朝偉'); Query OK, 5 rows affected (0.00 sec) Records: 5 Duplicates: 0 Warnings: 0 mysql> select * from stu; +------+-----+---------------+ | id | age | name | +------+-----+---------------+ | 1001 | 18 | 路人甲Java | | 1003 | 18 | 張學(xué)友 | | 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 | 張學(xué)友 | +------+-----+---------------+ 5 rows in set (0.00 sec)
按別名排序
mysql> select * from stu; +------+-----+---------------+ | id | age | name | +------+-----+---------------+ | 1001 | 18 | 路人甲Java | | 1003 | 18 | 張學(xué)友 | | 1004 | 20 | 張國榮 | | 1005 | 20 | 劉德華 | | 1010 | 19 | 梁朝偉 | +------+-----+---------------+ 5 rows in set (0.00 sec) mysql> select age '年齡',id as '學(xué)號' from stu order by 年齡 asc,學(xué)號 desc; +--------+--------+ | 年齡 | 學(xué)號 | +--------+--------+ | 18 | 1003 | | 18 | 1001 | | 19 | 1010 | | 20 | 1005 | | 20 | 1004 | +--------+--------+
按函數(shù)排序
有學(xué)生表(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 '學(xué)號', -> 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','張學(xué)友'),(1004,'1968-07-01','張國榮'),(1010,'1962-05-16','梁朝偉'); Query OK, 5 rows affected (0.00 sec) Records: 5 Duplicates: 0 Warnings: 0 mysql> mysql> SELECT * FROM student; +------+------------+---------------+ | id | birth | name | +------+------------+---------------+ | 1001 | 1990-10-10 | 路人甲Java | | 1003 | 1960-08-16 | 張學(xué)友 | | 1004 | 1968-07-01 | 張國榮 | | 1005 | 1960-03-01 | 劉德華 | | 1010 | 1962-05-16 | 梁朝偉 | +------+------------+---------------+ 5 rows in set (0.00 sec)
需求:按照出生年份升序、編號升序,查詢出編號、出生日期、出生年份、姓名,2種寫法如下:
mysql> SELECT id 編號,birth 出生日期,year(birth) 出生年份,name 姓名 from student ORDER BY year(birth) asc,id asc; +--------+--------------+--------------+---------------+ | 編號 | 出生日期 | 出生年份 | 姓名 | +--------+--------------+--------------+---------------+ | 1003 | 1960-08-16 | 1960 | 張學(xué)友 | | 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 | 張學(xué)友 | | 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)
說明:
year函數(shù):屬于日期函數(shù),可以獲取對應(yīng)日期中的年份。
上面使用了2種方式排序,第一種是在order by中使用了函數(shù),第二種是使用了別名排序。
where之后進(jìn)行排序
有訂單數(shù)據(jù)如下:
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: 0 mysql> 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)
需求:查詢訂單金額>=100的,按照訂單金額降序排序,顯示2列數(shù)據(jù),列頭:訂單編號、訂單金額,如下:
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)
limit介紹
limit用來限制select查詢返回的行數(shù),常用于分頁等操作。
語法:
select 列 from 表 limit [offset,] count;
說明:
- offset:表示偏移量,通俗點(diǎn)講就是跳過多少行,offset可以省略,默認(rèn)為0,表示跳過0行;范圍:[0,+∞)。
- count:跳過offset行之后開始取數(shù)據(jù),取count行記錄;范圍:[0,+∞)。
- limit中offset和count的值不能用表達(dá)式。
下面我們列一些常用的示例來加深理解。
獲取前n行記錄
select 列 from 表 limit 0,n; 或者 select 列 from 表 limit n;
示例,獲取訂單的前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: 0 mysql> 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)
獲取最大的一條記錄
我們需要獲取訂單金額最大的一條記錄,可以這么做:先按照金額降序,然后取第一條記錄,如下:
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)
獲取排名第n到m的記錄
我們需要先跳過n-1條記錄,然后取m-n+1條記錄,如下:
select 列 from 表 limit n-1,m-n+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)
分頁查詢
開發(fā)過程中,分頁我們經(jīng)常使用,分頁一般有2個(gè)參數(shù):
page:表示第幾頁,從1開始,范圍[1,+∞)
pageSize:每頁顯示多少條記錄,范圍[1,+∞)
如:page = 2,pageSize = 10,表示獲取第2頁10條數(shù)據(jù)。
我們使用limit實(shí)現(xiàn)分頁,語法如下:
select 列 from 表名 limit (page - 1) * pageSize,pageSize;
需求:我們按照訂單金額降序,每頁顯示2條,依次獲取所有訂單數(shù)據(jù)、第1頁、第2頁、第3頁數(shù)據(jù),如下:
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)
避免踩坑
limit中不能使用表達(dá)式
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 1 mysql> 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 1 mysql>
結(jié)論:limit后面只能夠跟明確的數(shù)字。
limit后面的2個(gè)數(shù)字不能為負(fù)數(shù)
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 1 mysql> 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 1 mysql> 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 1
排序分頁存在的坑
準(zhǔn)備數(shù)據(jù):
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: 0 mysql> 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)
下面我們按照b升序,每頁2條數(shù)據(jù),來獲取數(shù)據(jù)。
下面的sql依次為第1頁、第2頁、第3頁、第4頁、第5頁的數(shù)據(jù),如下:
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)
上面有2個(gè)問題:
問題1:看一下第2個(gè)sql和第3個(gè)sql,分別是第2頁和第3頁的數(shù)據(jù),結(jié)果出現(xiàn)了相同的數(shù)據(jù),是不是懵逼了。
問題2:整個(gè)表只有8條記錄,怎么會出現(xiàn)第5頁的數(shù)據(jù)呢,又懵逼了。
我們來分析一下上面的原因:主要是b字段存在相同的值,當(dāng)排序過程中存在相同的值時(shí),沒有其他排序規(guī)則時(shí),mysql懵逼了,不知道怎么排序了。
就像我們上學(xué)站隊(duì)一樣,按照身高排序,那身高一樣的時(shí)候如何排序呢?身高一樣的就亂排了。
建議:排序中存在相同的值時(shí),需要再指定一個(gè)排序規(guī)則,通過這種排序規(guī)則不存在二義性,比如上面可以再加上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)
看上面的結(jié)果,分頁數(shù)據(jù)都正常了,第5頁也沒有數(shù)據(jù)了。
總結(jié)
- order by … [asc|desc]用于對查詢結(jié)果排序,asc:升序,desc:降序,asc|desc可以省略,默認(rèn)為asc
- limit用來限制查詢結(jié)果返回的行數(shù),有2個(gè)參數(shù)(offset,count),offset:表示跳過多少行,count:表示跳過offset行之后取count行
- limit中offset可以省略,默認(rèn)值為0
- limit中offset 和 count都必須大于等于0
- limit中offset和count的值不能用表達(dá)式
- 分頁排序時(shí),排序不要有二義性,二義性情況下可能會導(dǎo)致分頁結(jié)果亂序,可以在后面追加一個(gè)主鍵排序
到此這篇關(guān)于Mysql排序和分頁(order by&limit)及存在的坑的文章就介紹到這了,更多相關(guān)Mysql排序和分頁內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
分享mysql的current_timestamp小坑及解決
這篇文章主要介紹了mysql的current_timestamp小坑及解決,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11MySQL通過自定義函數(shù)實(shí)現(xiàn)遞歸查詢父級ID或者子級ID
這篇文章主要介紹了MySQL通過自定義函數(shù)實(shí)現(xiàn)遞歸查詢父級ID或者子級ID,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-11-11一個(gè) 20 秒 SQL 慢查詢優(yōu)化處理方案
這篇文章主要分享一個(gè) 20 秒 SQL 慢查詢優(yōu)化的經(jīng)歷與處理方案,頁面無法正確獲取數(shù)據(jù),經(jīng)排查原來是接口調(diào)用超時(shí),而最后發(fā)現(xiàn)是因?yàn)镾QL查詢長達(dá)到20多秒而導(dǎo)致了問題的發(fā)生,下面來看問題具體介紹吧2022-01-01MySQL曝中間人攻擊Riddle漏洞可致用戶名密碼泄露的處理方法
Riddle漏洞存在于DBMS Oracle MySQL中,攻擊者可以利用漏洞和中間人身份竊取用戶名和密碼。下面小編給大家?guī)砹薓ySQL曝中間人攻擊Riddle漏洞可致用戶名密碼泄露的處理方法,需要的朋友參考下吧2018-01-01MySQL中IF()、IFNULL()、NULLIF()、ISNULL()函數(shù)的使用詳解
在MySQL中可以使用IF()、IFNULL()、NULLIF()、ISNULL()函數(shù)進(jìn)行流程的控制。本文就詳細(xì)的介紹這幾種方法,感興趣的可以了解一下2021-06-06Linux安裝MySQL教程(二進(jìn)制分發(fā)版)
這篇文章主要為大家詳細(xì)介紹了Linux安裝MySQL教程,二進(jìn)制分發(fā)版,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-02-02mysql導(dǎo)入導(dǎo)出數(shù)據(jù)的示例詳解
本文主要介紹了MySQL 導(dǎo)出和導(dǎo)入數(shù)據(jù)的幾種實(shí)現(xiàn)方式,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-05-05Mysql中事務(wù)ACID的實(shí)現(xiàn)原理詳解
這篇文章主要給大家介紹了關(guān)于Mysql中事務(wù)ACID實(shí)現(xiàn)原理的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用Mysql具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-05-05