分页功能是很常见的功能,特别是当数据量越来越大的时候,分页查询是必不可少的。实现分页功能有很多种方式,如果使用的ORM框架是mybatis的话,有开源的分页插件可以使用,如:Mybatis-PageHelper。如果不使用分页插件,那么就需要手动分页了,由于不同的数据库实现分页的SQL语句并不一致,如Mysql使用的是limit关键字,而Oracle使用的是rownum,所以本文本文讲解的分页方案只适用于Mysql数据库。

基于limit的分页方案

首先讲讲分页操作必须满足的几个要求:一个是有序性,一个是不重复。有序性可以看成是不重复的前提条件,因为假如数据是无序的,那么就不能保证多个分页之间是不重复的。因此分页操作往往需要先对数据进行排序,然后再加上分页条件。我们讲的第一种方案是基于limit的分页方案,也是很多分页插件使用的分页方案。我们先来看看我们的测试数据。

先看一下表结构:

mysql> desc user;
+-------+------------+------+-----+---------+-------+
| Field | Type       | Null | Key | Default | Extra |
+-------+------------+------+-----+---------+-------+
| id    | bigint(20) | NO   | PRI | NULL    |       |
| name  | char(50)   | NO   |     | NULL    |       |
+-------+------------+------+-----+---------+-------+
2 rows in set
复制代码

可以看到我们的user表只有2列,分别是bigint型的id和char型的name。

接下来看下表数据:

mysql> select count(*) from user;
+----------+
| count(*) |
+----------+
|    45116 |
+----------+
1 row in setmysql> select * from user order by id asc limit 10;
+----+--------+
| id | name   |
+----+--------+
|  0 | user_0 |
|  1 | user_1 |
|  2 | user_2 |
|  3 | user_3 |
|  4 | user_4 |
|  5 | user_5 |
|  6 | user_6 |
|  7 | user_7 |
|  8 | user_8 |
|  9 | user_9 |
+----+--------+
10 rows in set
复制代码

可以看到数据总行数大概45000条。

基于limit实现分页是比较简单的:

mysql> select * from user order by id asc limit 10000,10;
+-------+------------+
| id    | name       |
+-------+------------+
| 10000 | user_10000 |
| 10001 | user_10001 |
| 10002 | user_10002 |
| 10003 | user_10003 |
| 10004 | user_10004 |
| 10005 | user_10005 |
| 10006 | user_10006 |
| 10007 | user_10007 |
| 10008 | user_10008 |
| 10009 | user_10009 |
+-------+------------+
10 rows in set
复制代码

其中,limit后面的第一个参数表示下标,也就是从第10000行记录开始取,第二个参数表示总共取10行记录。

使用limit实现分页功能使用起来非常简单,但是有没有什么问题呢?

我们先来回顾一下前面说的分页需要满足的2个要素:有序性和不重复。上述的语句我们已经使用了order by 进行排序,所以是可以满足有序性的,但满足了不重复了吗?假设在查询当前页跟下一页之间插入了一条记录,且该数据的id小于当前页记录中最大的id,会怎么样呢?我们测试一下就知道了:

mysql> select * from user order by id asc limit 10000,10;
+-------+------------+
| id    | name       |
+-------+------------+
| 10000 | user_10000 |
| 10001 | user_10001 |
| 10002 | user_10002 |
| 10003 | user_10003 |
| 10004 | user_10004 |
| 10005 | user_10005 |
| 10006 | user_10006 |
| 10007 | user_10007 |
| 10008 | user_10008 |
| 10009 | user_10009 |
+-------+------------+
10 rows in setmysql> insert into user(id,name) values(-1,'user_-1');
Query OK, 1 row affected
mysql> select * from user order by id asc limit 10010,10;
+-------+------------+
| id    | name       |
+-------+------------+
| 10009 | user_10009 |
| 10010 | user_10010 |
| 10011 | user_10011 |
| 10012 | user_10012 |
| 10013 | user_10013 |
| 10014 | user_10014 |
| 10015 | user_10015 |
| 10016 | user_10016 |
| 10017 | user_10017 |
| 10018 | user_10018 |
+-------+------------+
10 rows in set
复制代码

可以看到,当我们在相邻的2页查询之间插入一条记录的时候,后面一页跟前面一页有记录重复了(id为10009的记录在相邻2页中都出现了)。原因在于插入一条记录之后,分页结构已经改变了,所以才会出现重复数据。

因此,使用limit进行分页似乎不是很优雅啊,接下来将介绍另外一种分页的写法。

基于limit与比较的分页方案

另外一种分页的写法可以这样考虑,比如我们要取的是从第10000行开始的10行记录,那么我们可以先把大于或等于10000行的数据查出来并排序,然后再取出前10行记录,这样也可以完成分页。接下来看具体的SQL语句:

mysql> select * from user where id >=10000 order by id asc limit 10;
+-------+------------+
| id    | name       |
+-------+------------+
| 10000 | user_10000 |
| 10001 | user_10001 |
| 10002 | user_10002 |
| 10003 | user_10003 |
| 10004 | user_10004 |
| 10005 | user_10005 |
| 10006 | user_10006 |
| 10007 | user_10007 |
| 10008 | user_10008 |
| 10009 | user_10009 |
+-------+------------+
10 rows in set
复制代码

那么这种写法可以防止上面出现的问题吗?我们做个试验就知道了。

mysql> select * from user where id >=10000 order by id asc limit 10;
+-------+------------+
| id    | name       |
+-------+------------+
| 10000 | user_10000 |
| 10001 | user_10001 |
| 10002 | user_10002 |
| 10003 | user_10003 |
| 10004 | user_10004 |
| 10005 | user_10005 |
| 10006 | user_10006 |
| 10007 | user_10007 |
| 10008 | user_10008 |
| 10009 | user_10009 |
+-------+------------+
10 rows in setmysql> insert into user(id,name) values(-4,'user_-4');
Query OK, 1 row affected
mysql> select * from user where id >=10010order by id asc limit 10;
+-------+------------+
| id    | name       |
+-------+------------+
| 10010 | user_10010 |
| 10011 | user_10011 |
| 10012 | user_10012 |
| 10013 | user_10013 |
| 10014 | user_10014 |
| 10015 | user_10015 |
| 10016 | user_10016 |
| 10017 | user_10017 |
| 10018 | user_10018 |
| 10019 | user_10019 |
+-------+------------+
10 rows in set
复制代码

可以看到,当在相邻的两页查询之间插入数据时,分页查询结果不会出现重复。其实也很好理解,因为虽然插入记录后,分页的结构变了,但是由于我们现在的分页查询是从固定的id开始查的,所以插入新的数据对后面的分页结果没有影响。

当然,这种分页查询也是有限制的。其只适用于用来排序的列具有唯一性的情况,在上述例子中,id列是主键,所以具有唯一性,故可以使用这种方式分页。如果用来排序的列不具有唯一性,比如说是时间戳,那么这种分页方式也可能出现重复,大家可以想想是为什么。

如果觉得这篇文章对你有帮助,可以扫描下方二维码,关注我的公众号“Java架构沉思录”。

如何优雅地实现分页查询相关推荐

  1. 面试:数据量很大,分页查询很慢,有什么优化方案?

    点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 来源 | cnblogs.com/youyoui/p/7 ...

  2. MySQL的limit用法和分页查询的性能分析及优化

    一.limit用法 在我们使用查询语句的时候,经常要返回前几条或者中间某几行数据,这个时候怎么办呢?不用担心,mysql已经为我们提供了这样一个功能. SELECT * FROM table LIMI ...

  3. 一对多分页查询mysql编写_一对多分页的SQL到底应该怎么写?

    1. 前言 MySQL一对多的数据分页是非常常见的需求,比如我们要查询商品和商品的图片信息.但是很多人会在这里遇到分页的误区,得到不正确的结果.今天就来分析并解决这个问题. 2. 问题分析 我们先创建 ...

  4. mysql分页总页数算法解析_详解MySQL的limit用法和分页查询语句的性能分析

    limit用法 在我们使用查询语句的时候,经常要返回前几条或者中间某几行数据,这个时候怎么办呢?不用担心,mysql已经为我们提供了这样一个功能. SELECT * FROM table LIMIT ...

  5. 自定义插件解决MyBatis-Plus like查询遇_ % \等字符需转译问题(含分页查询)

    我们使用MyBatis-Plus执行LIKE模糊查询时,若预处理参数包含_ % \等字符(欢迎补充),会查询出所有结果,这不是我们需要的. 不论写法是自定义SQL xxx like concat('% ...

  6. 几种常用数据库分页查询

    一.MySQL 1.Mysql的limit用法 在我们使用查询语句的时候,经常要返回前几条或者中间某几行数据,这个时候怎么办呢?不用担心,mysql已经为我们提供了这样一个功能. SELECT * F ...

  7. 最简单的 MyBatis Plus 的多表联接、分页查询实现方法

    一.前言 最近在加强 ITAEM 团队的一个 app 项目--学生教师学习交流平台 人员组成:安卓 + 前端 + 后台 后台 DAO 层借鉴了华工其他软件开发团队,使用了新颖强大的 MyBatisPl ...

  8. PageHelper 分页查询「实现篇」

    两种情况 分两种情况: 查出来为 PageQueryRespDTO 查出来直接是 Model 查出来为 PageQueryRespDTO public PageInfo<PageQueryRes ...

  9. [JAVA EE] JPA 查询用法:自定义查询,分页查询

    项目已上传:https://codechina.csdn.net/qq_36286039/javaee 自定义查询 问题:内置的crud功能不满足需求时如何添加自定义查询? 几种自定义查询方法 方法命 ...

最新文章

  1. ARM通用NEON多媒体处理SIMD引擎
  2. 学会python之后-学会Python后,人生简直开挂了!
  3. 5.20打卡 equals()方法与“==”的区别
  4. TCP三次握手及关闭时的2MSL分析
  5. C#.net调用Excel出现问题
  6. A - Greed CodeForces - 892A(水题)
  7. Linux有关Shell中if用法笔记
  8. window oracle 只有bak文件怎么恢复_一起来学习Oracle的备份恢复基础吧-4
  9. Response.Write具体介绍
  10. QT学习笔记(四):Qt5+MSVC编译 中文字符显示乱码问题解决
  11. Java项目课程06:系统实现-数据库
  12. 计算机管理文件的教学设计,《管理计算机中的文件》教学设计文件.doc
  13. HDFS的java API操作(基于Windows平台的Eclipse)
  14. 关于Java后台 HTML+CSS3 转换生成PDF文件问题求助!
  15. 10款开源网上教学课程管理系统
  16. 做机器学习算法工程师是什么样的工作体验?
  17. NSGA 2 学习笔记 -- crowding distance
  18. Java8新特性函数式编程
  19. python 月球上物体的体重,1.重量计算。月球上物体体重是在地球上的16.5%,假如你在地球上每年增长0.5KG,输出未来十年你在地球和月球上的体重状况...
  20. appinventor飞机大战案例_第一个AppInventor 开发案例 Hello Kitty(下)

热门文章

  1. 编译安装LAMP之配置httpd以FastCGI方式与php整合
  2. freemaker中配置文件freemarker.properties详解
  3. VMware Workstation 更改语言
  4. 浏览器是怎样工作的(一):基础知识
  5. Mono项目宣布将整合F#
  6. SharePoint 2007图文开发教程(3)---实现简单的WebPart
  7. 快乐的生活 2008-10-10 18:49:00
  8. Puppeteer爬虫初探
  9. 全新界面改版+实用功能上线 高德地图新版全体验
  10. 中国铁建内网漫游沦陷多个重要部门泄漏大量信息(redis+ssh-keygen免认证登录案例)...