Mysql系列第八讲 详解排序和分页(order by & limit)及存在的坑 SQL语句的一些应用语句 介绍三个开发技术小知识点 数据库系统概论(1) Mysql备份和恢复的类型,备份恢复的几种方法! --个人整理 MySql数据库基本操作(一) 前八月融合创新提升服务,大数据搭建信息网 长三角税收一体化按下“快进键” 一些建议:给当年刚做程序员的我 谁是物联网江湖明日之星?答案是新CIoT 企业信用有效“变现” 江苏银行物联网金融规模达185亿 2020年中国5G物联网行业:中国通信运营商5G投资达1.23万亿元-可下载 搭建 GFS 分布式文件系统-----实操 排障集锦:九九八十一难之第十五难!-------------史上最全rsync报错详解 docker系列1---docker隔离和限制技术 Linux系统安全及应用加固———最适合新手学,新手都能看懂!超详细的理论+超详细的实验!呕心沥血之作完成! LAMP架构集成ownCloud搭建私有云盘 Shell脚本正则表达式(一) Linux第一人称侵入进程的好地方 Windows DOS常用命令(持续更新完善) 大数据驱动新材料研发 变革从内而发,一站式大数据平台和多模数据库雄起 雕佛像、打棒球、开飞机、去北极,这个90后程序员有点不一样 12项特色功能构建施工现场“物联网” 400亿美元!英伟达收购Arm 软银套现离场物联网梦醒 卫星物联网再次引发热议,原因在这里 信用飞:航旅行业数字化前景可期,大数据预防砍头息乱象 大数据加持自动驾驶产业发展 上海智能网联汽车公共数据中心揭牌 恒久科技(002808.SZ):子公司闽保信息与鸿达兴业签署智能制造及大数据平台综合应用项目战略合作框架协议 工业平板电脑如何在物联网中发挥作用? 400亿美元!英伟达收购Arm 软银套现离场物联网梦醒 “数字心脏”动态解析消费密码——国家级消费市场大数据联合实验 重庆:大数据智能化牵引产业结构调整质效提升 分析物联网技术在智能家居中的应用场景 国家级消费市场大数据联合实验室在上海先行先试 山西吕梁培训搬迁户成为大数据标注员 昔日贫困户,玩转高科技 知乎热议:2020年程序员是否过剩? 柯建东:宁波抢抓工业物联网新机遇,要做好四个平台建设 旷视:城市物联网的实现,是从超级应用到操作系统的沉淀过程 排障集合———忘记原密码,如何重新修改MySQL登入密码 【Redis】主从复制 网易2021批笔试题解 msf工具之木马程序制作以及伪装 【JAVA】滴滴-2021校招在线笔试-DE数据开发试卷-0913 机器学习13 -- 无监督学习之自监督 2020年全国数学建模大赛 B题部分代码和模型的建立 jumpserver安装(centos) Redis 知识点总结 通过RabitMQ实现分布式事务 高焕堂《嵌入式UML设计》读书笔记_第一章 2020年中国5G在物联网行业:中国通信运营商5G投资达1.23万亿元(可下载)
您的位置:首页 >运维 >

Mysql系列第八讲 详解排序和分页(order by & limit)及存在的坑

Mysql系列第八讲

排序查询(order by)单字段排序多字段排序按别名排序按函数排序where之后进行排序 limit介绍获取前n行记录获取最大的一条记录获取排名第n到m的记录分页查询 避免踩坑limit中不能使用表达式limit后面的2个数字不能为负数排序分页存在的坑 总结

排序查询(order by)

电商中:我们想查看今天所有成交的订单,按照交易额从高到低排序,此时我们可以使用数据库中的排序功能来完成。

排序语法:

select 字段名 from 表名 order by 字段1 [asc|desc],字段2 [asc|desc];

需要排序的字段跟在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: 4Duplicates: 0Warnings: 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)

多字段排序

比如学生表,先按学生年龄降序,年龄相同时,再按学号升序,如下:

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: 5Duplicates: 0Warnings: 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)

按别名排序

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 |+--------+--------+

按函数排序

有学生表(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: 5Duplicates: 0Warnings: 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)

需求:按照出生年份升序、编号升序,查询出编号、出生日期、出生年份、姓名,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)

说明:

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: 6Duplicates: 0Warnings: 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)

需求:查询订单金额>=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)

limit介绍

limit用来限制select查询返回的行数,常用于分页等操作。

语法:

select 列 from 表 limit [offset,] count;

说明:

offset:表示偏移量,通俗点讲就是跳过多少行,offset可以省略,默认为0,表示跳过0行;范围:[0,+∞)。

count:跳过offset行之后开始取数据,取count行记录;范围:[0,+∞)。

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

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

获取前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: 6Duplicates: 0Warnings: 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)

获取最大的一条记录

我们需要获取订单金额最大的一条记录,可以这么做:先按照金额降序,然后取第一条记录,如下:

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)

分页查询

开发过程中,分页我们经常使用,分页一般有2个参数:

page:表示第几页,从1开始,范围[1,+∞)

pageSize:每页显示多少条记录,范围[1,+∞)

如:page = 2,pageSize = 10,表示获取第2页10条数据。

我们使用limit实现分页,语法如下:

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

需求:我们按照订单金额降序,每页显示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)

避免踩坑

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>

结论: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 1

排序分页存在的坑

准备数据:

mysql> insert into test1 (b) values (1),(2),(3),(4),(2),(2),(2),(2);Query OK, 8 rows affected (0.01 sec)Records: 8Duplicates: 0Warnings: 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)

下面我们按照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)

上面有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)

看上面的结果,分页数据都正常了,第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的值不能用表达式

分页排序时,排序不要有二义性,二义性情况下可能会导致分页结果乱序,可以在后面追加一个主键排序

易兮科技Java18级大连海事大学在校生
网络工程 & 国际经济与贸易 双学位
中山市易兮软件开发有限公司创始人
易兮科技团队负责人
Java语言爱好者

郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。