第8篇:排序和分页(orderby、limit)

star2017 1年前 ⋅ 432 阅读

打算提升sql技能的,可以加我微信itsoku,带你成为sql高手。

这是Mysql系列第8篇。

环境:mysql5.7.25,cmd命令中进行演示。

代码中被[]包含的表示可选,|符号分开的表示可选其一。

本章内容

排序查询(order by)

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

排序语法:

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

需要排序的字段跟在order by之后;

asc|desc表示排序的规则,asc:升序,desc:降序,默认为asc;

支持多个字段进行排序,多字段排序之间用逗号隔开。

单字段排序

  1. mysql> create table test2(a int,b varchar(10));
  2. Query OK, 0 rows affected (0.01 sec)
  3. mysql> insert into test2 values (10,'jack'),(8,'tom'),(5,'ready'),(100,'javacode');
  4. Query OK, 4 rows affected (0.00 sec)
  5. Records: 4 Duplicates: 0 Warnings: 0
  6. mysql> select * from test2;
  7. +------+----------+
  8. | a | b |
  9. +------+----------+
  10. | 10 | jack |
  11. | 8 | tom |
  12. | 5 | ready |
  13. | 100 | javacode |
  14. +------+----------+
  15. 4 rows in set (0.00 sec)
  16. mysql> select * from test2 order by a asc;
  17. +------+----------+
  18. | a | b |
  19. +------+----------+
  20. | 5 | ready |
  21. | 8 | tom |
  22. | 10 | jack |
  23. | 100 | javacode |
  24. +------+----------+
  25. 4 rows in set (0.00 sec)
  26. mysql> select * from test2 order by a desc;
  27. +------+----------+
  28. | a | b |
  29. +------+----------+
  30. | 100 | javacode |
  31. | 10 | jack |
  32. | 8 | tom |
  33. | 5 | ready |
  34. +------+----------+
  35. 4 rows in set (0.00 sec)
  36. mysql> select * from test2 order by a;
  37. +------+----------+
  38. | a | b |
  39. +------+----------+
  40. | 5 | ready |
  41. | 8 | tom |
  42. | 10 | jack |
  43. | 100 | javacode |
  44. +------+----------+
  45. 4 rows in set (0.00 sec)

多字段排序

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

  1. mysql> create table stu(id int not null comment '学号' primary key,age tinyint not null comment '年龄',name varchar(16) comment '姓名');
  2. Query OK, 0 rows affected (0.01 sec)
  3. mysql> insert into stu (id,age,name) values (1001,18,'路人甲Java'),(1005,20,'刘德华'),(1003,18,'张学友'),(1004,20,'张国荣'),(1010,19,'梁朝伟');
  4. Query OK, 5 rows affected (0.00 sec)
  5. Records: 5 Duplicates: 0 Warnings: 0
  6. mysql> select * from stu;
  7. +------+-----+---------------+
  8. | id | age | name |
  9. +------+-----+---------------+
  10. | 1001 | 18 | 路人甲Java |
  11. | 1003 | 18 | 张学友 |
  12. | 1004 | 20 | 张国荣 |
  13. | 1005 | 20 | 刘德华 |
  14. | 1010 | 19 | 梁朝伟 |
  15. +------+-----+---------------+
  16. 5 rows in set (0.00 sec)
  17. mysql> select * from stu order by age desc,id asc;
  18. +------+-----+---------------+
  19. | id | age | name |
  20. +------+-----+---------------+
  21. | 1004 | 20 | 张国荣 |
  22. | 1005 | 20 | 刘德华 |
  23. | 1010 | 19 | 梁朝伟 |
  24. | 1001 | 18 | 路人甲Java |
  25. | 1003 | 18 | 张学友 |
  26. +------+-----+---------------+
  27. 5 rows in set (0.00 sec)

按别名排序

  1. mysql> select * from stu;
  2. +------+-----+---------------+
  3. | id | age | name |
  4. +------+-----+---------------+
  5. | 1001 | 18 | 路人甲Java |
  6. | 1003 | 18 | 张学友 |
  7. | 1004 | 20 | 张国荣 |
  8. | 1005 | 20 | 刘德华 |
  9. | 1010 | 19 | 梁朝伟 |
  10. +------+-----+---------------+
  11. 5 rows in set (0.00 sec)
  12. mysql> select age '年龄',id as '学号' from stu order by 年龄 asc,学号 desc;
  13. +--------+--------+
  14. | 年龄 | 学号 |
  15. +--------+--------+
  16. | 18 | 1003 |
  17. | 18 | 1001 |
  18. | 19 | 1010 |
  19. | 20 | 1005 |
  20. | 20 | 1004 |
  21. +--------+--------+

按函数排序

有学生表(id:编号,birth:出生日期,name:姓名),如下:

  1. mysql> drop table if exists student;
  2. Query OK, 0 rows affected (0.01 sec)
  3. mysql> CREATE TABLE student (
  4. -> id int(11) NOT NULL COMMENT '学号',
  5. -> birth date NOT NULL COMMENT '出生日期',
  6. -> name varchar(16) DEFAULT NULL COMMENT '姓名',
  7. -> PRIMARY KEY (id)
  8. -> );
  9. Query OK, 0 rows affected (0.01 sec)
  10. 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','梁朝伟');
  11. Query OK, 5 rows affected (0.00 sec)
  12. Records: 5 Duplicates: 0 Warnings: 0
  13. mysql>
  14. mysql> SELECT * FROM student;
  15. +------+------------+---------------+
  16. | id | birth | name |
  17. +------+------------+---------------+
  18. | 1001 | 1990-10-10 | 路人甲Java |
  19. | 1003 | 1960-08-16 | 张学友 |
  20. | 1004 | 1968-07-01 | 张国荣 |
  21. | 1005 | 1960-03-01 | 刘德华 |
  22. | 1010 | 1962-05-16 | 梁朝伟 |
  23. +------+------------+---------------+
  24. 5 rows in set (0.00 sec)

需求:按照出生年份升序、编号升序,查询出编号、出生日期、出生年份、姓名,2种写法如下:

  1. mysql> SELECT id 编号,birth 出生日期,year(birth) 出生年份,name 姓名 from student ORDER BY year(birth) asc,id asc;
  2. +--------+--------------+--------------+---------------+
  3. | 编号 | 出生日期 | 出生年份 | 姓名 |
  4. +--------+--------------+--------------+---------------+
  5. | 1003 | 1960-08-16 | 1960 | 张学友 |
  6. | 1005 | 1960-03-01 | 1960 | 刘德华 |
  7. | 1010 | 1962-05-16 | 1962 | 梁朝伟 |
  8. | 1004 | 1968-07-01 | 1968 | 张国荣 |
  9. | 1001 | 1990-10-10 | 1990 | 路人甲Java |
  10. +--------+--------------+--------------+---------------+
  11. 5 rows in set (0.00 sec)
  12. mysql> SELECT id 编号,birth 出生日期,year(birth) 出生年份,name 姓名 from student ORDER BY 出生年份 asc,id asc;
  13. +--------+--------------+--------------+---------------+
  14. | 编号 | 出生日期 | 出生年份 | 姓名 |
  15. +--------+--------------+--------------+---------------+
  16. | 1003 | 1960-08-16 | 1960 | 张学友 |
  17. | 1005 | 1960-03-01 | 1960 | 刘德华 |
  18. | 1010 | 1962-05-16 | 1962 | 梁朝伟 |
  19. | 1004 | 1968-07-01 | 1968 | 张国荣 |
  20. | 1001 | 1990-10-10 | 1990 | 路人甲Java |
  21. +--------+--------------+--------------+---------------+
  22. 5 rows in set (0.00 sec)

说明:

year函数:属于日期函数,可以获取对应日期中的年份。

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

where之后进行排序

有订单数据如下:

  1. mysql> drop table if exists t_order;
  2. Query OK, 0 rows affected, 1 warning (0.00 sec)
  3. mysql> create table t_order(
  4. -> id int not null auto_increment comment '订单编号',
  5. -> price decimal(10,2) not null default 0 comment '订单金额',
  6. -> primary key(id)
  7. -> )comment '订单表';
  8. Query OK, 0 rows affected (0.01 sec)
  9. mysql> insert into t_order (price) values (88.95),(100.68),(500),(300),(20.88),(200.5);
  10. Query OK, 6 rows affected (0.00 sec)
  11. Records: 6 Duplicates: 0 Warnings: 0
  12. mysql> select * from t_order;
  13. +----+--------+
  14. | id | price |
  15. +----+--------+
  16. | 1 | 88.95 |
  17. | 2 | 100.68 |
  18. | 3 | 500.00 |
  19. | 4 | 300.00 |
  20. | 5 | 20.88 |
  21. | 6 | 200.50 |
  22. +----+--------+
  23. 6 rows in set (0.00 sec)

需求:查询订单金额>=100的,按照订单金额降序排序,显示2列数据,列头:订单编号、订单金额,如下:

  1. mysql> select a.id 订单编号,a.price 订单金额 from t_order a where a.price>=100 order by a.price desc;
  2. +--------------+--------------+
  3. | 订单编号 | 订单金额 |
  4. +--------------+--------------+
  5. | 3 | 500.00 |
  6. | 4 | 300.00 |
  7. | 6 | 200.50 |
  8. | 2 | 100.68 |
  9. +--------------+--------------+
  10. 4 rows in set (0.00 sec)

limit介绍

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

语法:

  1. select from limit [offset,] count;

说明:

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

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

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

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

获取前n行记录

  1. select from limit 0,n;
  2. 或者
  3. select from limit n;

示例,获取订单的前2条记录,如下:

  1. mysql> create table t_order(
  2. -> id int not null auto_increment comment '订单编号',
  3. -> price decimal(10,2) not null default 0 comment '订单金额',
  4. -> primary key(id)
  5. -> )comment '订单表';
  6. Query OK, 0 rows affected (0.01 sec)
  7. mysql> insert into t_order (price) values (88.95),(100.68),(500),(300),(20.88),(200.5);
  8. Query OK, 6 rows affected (0.01 sec)
  9. Records: 6 Duplicates: 0 Warnings: 0
  10. mysql> select * from t_order;
  11. +----+--------+
  12. | id | price |
  13. +----+--------+
  14. | 1 | 88.95 |
  15. | 2 | 100.68 |
  16. | 3 | 500.00 |
  17. | 4 | 300.00 |
  18. | 5 | 20.88 |
  19. | 6 | 200.50 |
  20. +----+--------+
  21. 6 rows in set (0.00 sec)
  22. mysql> select a.id 订单编号,a.price 订单金额 from t_order a limit 2;
  23. +--------------+--------------+
  24. | 订单编号 | 订单金额 |
  25. +--------------+--------------+
  26. | 1 | 88.95 |
  27. | 2 | 100.68 |
  28. +--------------+--------------+
  29. 2 rows in set (0.00 sec)
  30. mysql> select a.id 订单编号,a.price 订单金额 from t_order a limit 0,2;
  31. +--------------+--------------+
  32. | 订单编号 | 订单金额 |
  33. +--------------+--------------+
  34. | 1 | 88.95 |
  35. | 2 | 100.68 |
  36. +--------------+--------------+
  37. 2 rows in set (0.00 sec)

获取最大的一条记录

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

  1. mysql> select a.id 订单编号,a.price 订单金额 from t_order a order by a.price desc;
  2. +--------------+--------------+
  3. | 订单编号 | 订单金额 |
  4. +--------------+--------------+
  5. | 3 | 500.00 |
  6. | 4 | 300.00 |
  7. | 6 | 200.50 |
  8. | 2 | 100.68 |
  9. | 1 | 88.95 |
  10. | 5 | 20.88 |
  11. +--------------+--------------+
  12. 6 rows in set (0.00 sec)
  13. mysql> select a.id 订单编号,a.price 订单金额 from t_order a order by a.price desc limit 1;
  14. +--------------+--------------+
  15. | 订单编号 | 订单金额 |
  16. +--------------+--------------+
  17. | 3 | 500.00 |
  18. +--------------+--------------+
  19. 1 row in set (0.00 sec)
  20. mysql> select a.id 订单编号,a.price 订单金额 from t_order a order by a.price desc limit 0,1;
  21. +--------------+--------------+
  22. | 订单编号 | 订单金额 |
  23. +--------------+--------------+
  24. | 3 | 500.00 |
  25. +--------------+--------------+
  26. 1 row in set (0.00 sec)

获取排名第n到m的记录

我们需要先跳过n-1条记录,然后取m-n+1条记录,如下:

  1. select from limit n-1,m-n+1;

如:我们想获取订单金额最高的3到5名的记录,我们需要跳过2条,然后获取3条记录,如下:

  1. mysql> select a.id 订单编号,a.price 订单金额 from t_order a order by a.price desc;
  2. +--------------+--------------+
  3. | 订单编号 | 订单金额 |
  4. +--------------+--------------+
  5. | 3 | 500.00 |
  6. | 4 | 300.00 |
  7. | 6 | 200.50 |
  8. | 2 | 100.68 |
  9. | 1 | 88.95 |
  10. | 5 | 20.88 |
  11. +--------------+--------------+
  12. 6 rows in set (0.00 sec)
  13. mysql> select a.id 订单编号,a.price 订单金额 from t_order a order by a.price desc limit 2,3;
  14. +--------------+--------------+
  15. | 订单编号 | 订单金额 |
  16. +--------------+--------------+
  17. | 6 | 200.50 |
  18. | 2 | 100.68 |
  19. | 1 | 88.95 |
  20. +--------------+--------------+
  21. 3 rows in set (0.00 sec)

分页查询

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

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

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

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

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

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

需求:我们按照订单金额降序,每页显示2条,依次获取所有订单数据、第1页、第2页、第3页数据,如下:

  1. mysql> select a.id 订单编号,a.price 订单金额 from t_order a order by a.price desc;
  2. +--------------+--------------+
  3. | 订单编号 | 订单金额 |
  4. +--------------+--------------+
  5. | 3 | 500.00 |
  6. | 4 | 300.00 |
  7. | 6 | 200.50 |
  8. | 2 | 100.68 |
  9. | 1 | 88.95 |
  10. | 5 | 20.88 |
  11. +--------------+--------------+
  12. 6 rows in set (0.00 sec)
  13. mysql> select a.id 订单编号,a.price 订单金额 from t_order a order by a.price desc limit 0,2;
  14. +--------------+--------------+
  15. | 订单编号 | 订单金额 |
  16. +--------------+--------------+
  17. | 3 | 500.00 |
  18. | 4 | 300.00 |
  19. +--------------+--------------+
  20. 2 rows in set (0.00 sec)
  21. mysql> select a.id 订单编号,a.price 订单金额 from t_order a order by a.price desc limit 2,2;
  22. +--------------+--------------+
  23. | 订单编号 | 订单金额 |
  24. +--------------+--------------+
  25. | 6 | 200.50 |
  26. | 2 | 100.68 |
  27. +--------------+--------------+
  28. 2 rows in set (0.00 sec)
  29. mysql> select a.id 订单编号,a.price 订单金额 from t_order a order by a.price desc limit 4,2;
  30. +--------------+--------------+
  31. | 订单编号 | 订单金额 |
  32. +--------------+--------------+
  33. | 1 | 88.95 |
  34. | 5 | 20.88 |
  35. +--------------+--------------+
  36. 2 rows in set (0.00 sec)

避免踩坑

limit中不能使用表达式

  1. mysql> select * from t_order where limit 1,4+1;
  2. 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
  3. mysql> select * from t_order where limit 1+0;
  4. 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
  5. mysql>

结论:limit后面只能够跟明确的数字。

limit后面的2个数字不能为负数

  1. mysql> select * from t_order where limit -1;
  2. 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
  3. mysql> select * from t_order where limit 0,-1;
  4. 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
  5. mysql> select * from t_order where limit -1,-1;
  6. 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

排序分页存在的坑

准备数据:

  1. mysql> insert into test1 (b) values (1),(2),(3),(4),(2),(2),(2),(2);
  2. Query OK, 8 rows affected (0.01 sec)
  3. Records: 8 Duplicates: 0 Warnings: 0
  4. mysql> select * from test1;
  5. +---+---+
  6. | a | b |
  7. +---+---+
  8. | 1 | 1 |
  9. | 2 | 2 |
  10. | 3 | 3 |
  11. | 4 | 4 |
  12. | 5 | 2 |
  13. | 6 | 2 |
  14. | 7 | 2 |
  15. | 8 | 2 |
  16. +---+---+
  17. 8 rows in set (0.00 sec)
  18. mysql> select * from test1 order by b asc;
  19. +---+---+
  20. | a | b |
  21. +---+---+
  22. | 1 | 1 |
  23. | 2 | 2 |
  24. | 5 | 2 |
  25. | 6 | 2 |
  26. | 7 | 2 |
  27. | 8 | 2 |
  28. | 3 | 3 |
  29. | 4 | 4 |
  30. +---+---+
  31. 8 rows in set (0.00 sec)

下面我们按照b升序,每页2条数据,来获取数据。

下面的sql依次为第1页、第2页、第3页、第4页、第5页的数据,如下:

  1. mysql> select * from test1 order by b asc limit 0,2;
  2. +---+---+
  3. | a | b |
  4. +---+---+
  5. | 1 | 1 |
  6. | 2 | 2 |
  7. +---+---+
  8. 2 rows in set (0.00 sec)
  9. mysql> select * from test1 order by b asc limit 2,2;
  10. +---+---+
  11. | a | b |
  12. +---+---+
  13. | 8 | 2 |
  14. | 6 | 2 |
  15. +---+---+
  16. 2 rows in set (0.00 sec)
  17. mysql> select * from test1 order by b asc limit 4,2;
  18. +---+---+
  19. | a | b |
  20. +---+---+
  21. | 6 | 2 |
  22. | 7 | 2 |
  23. +---+---+
  24. 2 rows in set (0.00 sec)
  25. mysql> select * from test1 order by b asc limit 6,2;
  26. +---+---+
  27. | a | b |
  28. +---+---+
  29. | 3 | 3 |
  30. | 4 | 4 |
  31. +---+---+
  32. 2 rows in set (0.00 sec)
  33. mysql> select * from test1 order by b asc limit 7,2;
  34. +---+---+
  35. | a | b |
  36. +---+---+
  37. | 4 | 4 |
  38. +---+---+
  39. 1 row in set (0.00 sec)

上面有2个问题:

问题1:看一下第2个sql和第3个sql,分别是第2页和第3页的数据,结果出现了相同的数据,是不是懵逼了。

问题2:整个表只有8条记录,怎么会出现第5页的数据呢,又懵逼了。

我们来分析一下上面的原因:主要是b字段存在相同的值,当排序过程中存在相同的值时,没有其他排序规则时,mysql懵逼了,不知道怎么排序了。

就像我们上学站队一样,按照身高排序,那身高一样的时候如何排序呢?身高一样的就乱排了。

建议:排序中存在相同的值时,需要再指定一个排序规则,通过这种排序规则不存在二义性,比如上面可以再加上a降序,如下:

  1. mysql> select * from test1 order by b asc,a desc;
  2. +---+---+
  3. | a | b |
  4. +---+---+
  5. | 1 | 1 |
  6. | 8 | 2 |
  7. | 7 | 2 |
  8. | 6 | 2 |
  9. | 5 | 2 |
  10. | 2 | 2 |
  11. | 3 | 3 |
  12. | 4 | 4 |
  13. +---+---+
  14. 8 rows in set (0.00 sec)
  15. mysql> select * from test1 order by b asc,a desc limit 0,2;
  16. +---+---+
  17. | a | b |
  18. +---+---+
  19. | 1 | 1 |
  20. | 8 | 2 |
  21. +---+---+
  22. 2 rows in set (0.00 sec)
  23. mysql> select * from test1 order by b asc,a desc limit 2,2;
  24. +---+---+
  25. | a | b |
  26. +---+---+
  27. | 7 | 2 |
  28. | 6 | 2 |
  29. +---+---+
  30. 2 rows in set (0.00 sec)
  31. mysql> select * from test1 order by b asc,a desc limit 4,2;
  32. +---+---+
  33. | a | b |
  34. +---+---+
  35. | 5 | 2 |
  36. | 2 | 2 |
  37. +---+---+
  38. 2 rows in set (0.00 sec)
  39. mysql> select * from test1 order by b asc,a desc limit 6,2;
  40. +---+---+
  41. | a | b |
  42. +---+---+
  43. | 3 | 3 |
  44. | 4 | 4 |
  45. +---+---+
  46. 2 rows in set (0.00 sec)
  47. mysql> select * from test1 order by b asc,a desc limit 8,2;
  48. 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的值不能用表达式
  • 分页排序时,排序不要有二义性,二义性情况下可能会导致分页结果乱序,可以在后面追加一个主键排序

最新资料

更多内容请访问:IT源点

相关文章推荐

全部评论: 0

    我有话说: