数据分页处理小陷阱

数据分页处理小陷阱

一、背景

很多时候,我们可能会遇到类似这样的一种需求。起一个定时,把符合某种状态的数据从数据表查出处理后更新回数据库表,同时因为数据量大需要做分页处理。

假设我们有这样一个表。
table1

idnameamtstatus
1zhangsan12success
2lisi113init
3lisi212init
4lisi216success
5lisi317init
6lisi419success
7lisi5120init
8lisi61success
9lisi84init
10lisi0123init

我们会使用这样的sql分页处理:

//数据结果集总条数,依此进行分页
select count(*) from table1 where status = 'init' 

// mysql分页,按照每2条分页
select * from table1 where status = 'init' 
 limit 0,2

// db2  按照每2条分页
SELECT * FROM (
    select 
    ROW_NUMBER() OVER(ORDER BY P.ID DESC) AS ROWNUM,
    * from table1 where status = 'init' 
) A WHERE
    ROWNUM > 0
    AND ROWNUM <= 2

我们通过上面sql进行分页处理,处理完的每条数据,状态更新为success。实际上是有问题的。我们分析下,我们每处理完一条数据,sql结果集其实都发生了变化,我们的分页信息其实已经过时。

  1. select * from table1 where status = 'init' 结果集为ID为2,3,5,7,9,10,
总条数:6
分页:
第一页行号边界:0 2    对应数据ID:2,3
第二页行号边界:2 4    对应数据ID:5,7
第三页行号边界:4 6    对应数据ID:9,10
  1. 当我们处理完第一条ID为2的那条,状态变为了success,在不分页的情况下这条sql结果集变为了3,5,7,9,10。当我们查询第二页时行号2,4对应的记录为9,10。
第二次查询分页sql,实际得到的记录:
行号:2,4     数据记录ID:9,10  我们期望的数据记录是ID:5,7
第三次查询分页sql,没有查到任何记录。

总结:按照行号分页,行号对应的记录是动态变化的,查询到的数据是不全的,错乱的。比如上面例子,ID为5,7的记录并没有被查询到。

二、解决

1. 根据ID进行分页

通过上面的分页,我们知道使用行号分页,是不可行的。由于我们的ID是一般都是顺序自增(如果不是顺序的不能使用下面的方法)的我们使用ID进行分页是可以避免上面问题,因为ID是固定不变的。

总条数:6
分页:
第一页行号边界:0 2    对应数据ID:2,3
第二页行号边界:2 4    对应数据ID:5,7
第三页行号边界:4 6    对应数据ID:9,10

1. 第一页sql:
    select 
    * from table1 where status = 'init' 
    WHERE
    ID >  2 and ID <= 3
2. 第二页sqlselect 
    * from table1 where status = 'init' 
    WHERE
    ID > 5 and ID <= 7
...

2. 如何获得分页ID的边界

使用ID分页,由于ID的值对应的记录是固定的,所以使用ID分页不会影响结果集。那么如何确定每一页的ID的边界值呢,首先符合条件的数据的ID的分布是不规则的,所以我们依然要通过行号确定分页的边界。

第一页行号边界:0 2    对应数据ID:2,3
第二页行号边界:2 4    对应数据ID:5,7
第三页行号边界:4 6    对应数据ID:9,10

我们只需要知道0,2,4,6四个行号对应的ID。参考如下sql:

SELECT ROWNUM FROM (
    select 
    ROW_NUMBER() OVER(ORDER BY P.ID DESC) AS ROWNUM,
    * from table1 where status = 'init' 
) A WHERE
    ROWNUM in (0,2,4,6)

有了每一页的边界的ID,我们就可以放心的一边分页查询,一边更新数据处理状态了。

三、分享一个分页的工具类



import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;

/**
 * @description:分页通用类
 * @author: xiaobin.liu
 * @date: 17/12/19
 * @time: 上午11:48
 */
public class Pager implements Serializable {

    private static final long serialVersionUID = -4694628008139971984L;

    /**
     * 构造
     * @param recordTotal   总记录数
     */
    public Pager(int recordTotal) {
        this(1, 10, recordTotal);
    }

    /**
     * 构造
     * @param pageSize      每页数量
     * @param recordTotal   总记录数
     */
    public Pager( int pageSize, int recordTotal) {
        this(1, pageSize, recordTotal);
    }

    /**
     * 构造
     * @param currentPage   当前页
     * @param pageSize      每页数量
     * @param recordTotal   总记录数
     */
    public Pager(int currentPage, int pageSize, int recordTotal) {
        this.currentPage = currentPage;
        this.pageSize = pageSize;
        this.recordTotal = recordTotal;
        otherAttr();
    }

    /**
     * currentPage 当前页
     */
    private int currentPage ;
    /**
     * pageSize 每页大小
     */
    private int pageSize ;
    /**
     * pageTotal 总页数
     */
    private int pageTotal;
    /**
     * recordTotal 总条数
     */
    private int recordTotal;
    /**
     * previousPage 前一页
     */
    private int previousPage;
    /**
     * nextPage 下一页
     */
    private int nextPage;
    /**
     * firstPage 第一页
     */
    private int firstPage = 1;
    /**
     * lastPage 最后一页
     */
    private int lastPage;
    /**
     * 当前页范围,下限,从0开始。SQL中应大于此值
     */
    private int lowRowNum;
    /**
     * 当前页范围,上限,最大recordTotal,SQL小于等于此值
     */
    private int highRowNum;

    /**
     * 设置其他参数
     *
     * @author kangxu
     *
     */
    private void otherAttr() {
        // 总页数
        this.pageTotal = this.recordTotal % this.pageSize > 0 ? this.recordTotal / this.pageSize + 1 : this.recordTotal / this.pageSize;
        // 第一页
        this.firstPage = 1;
        // 最后一页
        this.lastPage = this.pageTotal;
        refreshPager();
    }

    /**
     * 设置其他参数
     *
     * @author kangxu
     *
     */
    private void refreshPager() {
        // 前一页
        if (this.currentPage > 1) {
            this.previousPage = this.currentPage - 1;
        } else {
            this.previousPage = this.firstPage;
        }
        // 下一页
        if (this.currentPage < this.lastPage) {
            this.nextPage = this.currentPage + 1;
        } else {
            this.nextPage = this.lastPage;
        }
        //范围 根据当前页计算
        this.lowRowNum = this.pageSize * (this.currentPage - 1) ;
        this.highRowNum = this.currentPage * this.pageSize ;
        if (this.highRowNum > this.recordTotal) {
            this.highRowNum = this.recordTotal;
        }
    }

    // 放开私有属性
    public int getCurrentPage() {
        return currentPage;
    }

    public int getPageSize() {
        return pageSize;
    }

    public int getPageTotal() {
        return pageTotal;
    }

    public int getRecordTotal() {
        return recordTotal;
    }

    public int getPreviousPage() {
        return previousPage;
    }

    public int getNextPage() {
        return nextPage;
    }

    public int getFirstPage() {
        return firstPage;
    }

    public int getLastPage() {
        return lastPage;
    }

    public int getLowRowNum() {
        return lowRowNum;
    }

    public int getHighRowNum() {
        return highRowNum;
    }

    /**
     * 翻上一页
     */
    public Pager pageUp() {
        return goToPage(previousPage);
    }

    /**
     * 翻下一页
     */
    public Pager pageDown() {
        return goToPage(nextPage);
    }

    /**
     * 翻到第一页
     */
    public Pager gotoFirstPage() {
        return goToPage(firstPage);
    }

    /**
     * 翻到最后一页
     */
    public Pager gotoLastPage() {
        return goToPage(lastPage);
    }

    /**
     * 翻到指定页
     */
    public Pager goToPage(int pageNo) {
        if (pageNo <= 0) {
            this.currentPage = this.firstPage;
        } else if (pageNo > this.lastPage) {
            this.currentPage = this.lastPage;
        } else {
            this.currentPage = pageNo;
        }
        refreshPager();
        return this;
    }

    @Override
    public String toString() {
        return "Pager [currentPage=" + currentPage + ", pageSize=" + pageSize
                + ", pageTotal=" + pageTotal + ", recordTotal=" + recordTotal
                + ", previousPage=" + previousPage + ", nextPage=" + nextPage
                + ", firstPage=" + firstPage + ", lastPage=" + lastPage
                + ", lowRowNum=" + lowRowNum + ", highRowNum=" + highRowNum
                + "]";
    }

    public static void main(String[] args) {

        //创建一个分页
        Pager pager = new Pager(62);

        System.out.println(pager);
        System.out.println(pager.goToPage(4));
        System.out.println(pager.pageDown());
        System.out.println(pager.pageUp());
        System.out.println(pager.gotoFirstPage());
        System.out.println(pager.gotoLastPage());


        System.out.println(pager.goToPage(8));


        System.out.println(pager.gotoFirstPage());
        System.out.println("--------");
        for (int loopFlag = pager.getCurrentPage();loopFlag <= pager.getPageTotal() ;pager.pageDown(),loopFlag++) {
            System.out.println(pager);
        }

        // 遍历分页
        System.out.println("--------");
        Pager pager1 = new Pager(62);
        Set<Long> rownumSet = new HashSet<Long>();
        rownumSet.add(Long.valueOf(pager1.getLowRowNum()));
        for (int loopFlag = pager1.getCurrentPage();loopFlag <= pager1.getPageTotal() ;pager1.pageDown(),loopFlag++) {
            System.out.println(pager1);
            rownumSet.add(Long.valueOf(pager1.getHighRowNum()));
        }
        for (Long id : rownumSet) {
            System.out.println(id);
        }

    }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值