几种分页方式分析

一.逻辑分页
 
1.逻辑分页的第一种方式,利用ResultSet的滚动分页。步骤如下:
 a.根据条件sql查询数据库。
 b.得到ResultSet的结果集,由于ResultSet带有游标,因此可以使用其next()方法来指向下一条记录。
 c.利用next()方法,得到分页所需的结果集。

这种分页方式依靠的是对结果集的算法来分页,因此通常被称为“逻辑分页”。

代码如下:

/** *//**

 * TestPageResultSetDAO.java

 *

 * Copyright 2008. All Rights Reserved.

 */

package com.cosmow.pageresultset.dao;

import java.sql.Connection;

import java.sql.DriverManager;

import java.sql.PreparedStatement;

import java.sql.ResultSet;

import java.sql.SQLException;

import java.util.ArrayList;

import java.util.List;

import com.cosmow.pageresultset.entity.Bars;

/** *//**

 * TODO dao class TestPageResultSetDAO

 * 

 * Revision History

 * 

 * 2008-7-7,Cosmo,created it

 */

public class TestPageResultSetDAO {

    private final String FIND_BARS_PAGE = "SELECT * FROM YYBARS ORDER BY id";

    /** *//**

     * 提供JDBC连接方法,返回一个Connection的实例

     * 

     * @return

     * @throws SQLException

     */

    private Connection getConnection() throws SQLException {

        try {

            final String url = "jdbc:oracle:thin:@localhost:1521:ORCL";

            final String user = "store";

            final String password = "store_password";

            Class.forName("oracle.jdbc.driver.OracleDriver");

            Connection con = DriverManager.getConnection(url, user, password);

            return con;

        } catch (ClassNotFoundException e) {

            throw new SQLException(e.getMessage());

        }

    }

    /** *//**

     * 逻辑分页方法一,该方法使用移位(rs.next)来进行

     * 

     * @param currentPage

     *            当前页

     * @param showRows

     *            一页显示的数据量

     */

    public List<Bars> pageListOne(int currentPage, int showRows) {

        Connection con = null;

        PreparedStatement ps = null;

        ResultSet rs = null;

        ArrayList<Bars> resultList = new ArrayList<Bars>();

        try {

            con = getConnection();

            ps = con.prepareStatement(FIND_BARS_PAGE);

            rs = ps.executeQuery();

            // 过滤结果集的变量

            int skipBegin = (currentPage - 1) * showRows;

            int skipEnd = currentPage * showRows;

            // 翻页计数器

            int currentNum = 0;

            // 当返回结果集中有记录时

            while (rs.next()) {

                // 以下情况将保证在结果集中有记录时的应用

                if (currentNum >= skipBegin && currentNum < skipEnd) {

                    Bars bar = new Bars();

                    bar.setId(rs.getLong("id"));

                    bar.setName(rs.getString("name"));

                    bar.setType(rs.getInt("type"));

                    bar.setCreatorId(rs.getLong("creator_id"));

                    resultList.add(bar);

                    if (currentNum == skipEnd - 1)

                        break;

                }

                currentNum++;

            }

        } catch (SQLException e) {

            e.printStackTrace();

        } finally {

            try {

                if (rs != null)

                    rs.close();

                if (ps != null)

                    ps.close();

                if (con != null)

                    con.close();

            } catch (SQLException e) {

                e.printStackTrace();

            }

        }

        return resultList;

    }

}

2.逻辑分页的第二种方式
利用Scrollable ResultSets(可滚动结果集合)来快速定位到某个游标所指定的记录行,所使用的是ResultSet的absolute()方法。
改进代码如下:

/** *//**

 * TestPageResultSetDAO.java

 *

 * Copyright 2008. All Rights Reserved.

 */

package com.cosmow.pageresultset.dao;

import java.sql.Connection;

import java.sql.DriverManager;

import java.sql.PreparedStatement;

import java.sql.ResultSet;

import java.sql.SQLException;

import java.util.ArrayList;

import java.util.List;

import com.cosmow.pageresultset.entity.Bars;

/** *//**

 * TODO dao class TestPageResultSetDAO

 * 

 * Revision History

 * 

 * 2008-7-7,Cosmo,created it

 */

public class TestPageResultSetDAO {

    private final String FIND_BARS_PAGE = "SELECT * FROM YYBARS ORDER BY id";

    /** *//**

     * 提供JDBC连接方法,返回一个Connection的实例

     * 

     * @return

     * @throws SQLException

     */

    private Connection getConnection() throws SQLException {

        try {

            final String url = "jdbc:oracle:thin:@localhost:1521:ORCL";

            final String user = "store";

            final String password = "store_password";

            Class.forName("oracle.jdbc.driver.OracleDriver");

            Connection con = DriverManager.getConnection(url, user, password);

            return con;

        } catch (ClassNotFoundException e) {

            throw new SQLException(e.getMessage());

        }

    }

    /** *//**

     * 逻辑分页方法二,使用absolute()方法分页

     * 

     * @param currentPage

     *            当前页

     * @param showRows

     *            一页显示的数据量

     */

    public List<Bars> pageListTwo(int currentPage, int showRows) {

        Connection con = null;

        PreparedStatement ps = null;

        ResultSet rs = null;

        ArrayList<Bars> resultList = new ArrayList<Bars>();

        try {

            con = getConnection();

            ps = con.prepareStatement(FIND_BARS_PAGE);

            rs = ps.executeQuery();

            // 过滤结果集的变量

            int skipBegin = (currentPage - 1) * showRows;

            int skipEnd = currentPage * showRows;

            // 利用rs.absolute进行定位

            if (!rs.absolute(skipBegin))

                return resultList;

            // 当返回结果集中有记录时

            while (rs.next()) {

                // 以下情况将保证在结果集中有记录时的应用

                if (skipBegin < skipEnd) {

                    Bars bar = new Bars();

                    bar.setId(rs.getLong("id"));

                    bar.setName(rs.getString("name"));

                    bar.setType(rs.getInt("type"));

                    bar.setCreatorId(rs.getLong("creator_id"));

                    resultList.add(bar);

                    if (skipBegin == skipEnd - 1)

                        break;

                }

            }

        } catch (SQLException e) {

            e.printStackTrace();

        } finally {

            try {

                if (rs != null)

                    rs.close();

                if (ps != null)

                    ps.close();

                if (con != null)

                    con.close();

            } catch (SQLException e) {

                e.printStackTrace();

            }

        }

        return resultList;

    }

}

虽然和第一种方式区别不大,单效率比ResultSet滚动要好,但是absolute()方法并不是所有jdbc驱动都支持。
可用如下代码测试当前jdbc驱动是否支持可滚动结果集:

int type = rs.getType();
if (type == ResultSet.TYPE_SCROLL_INSENSITIVE || type == ResultSet.TYPE_SCROLL_SENSITIVE)
    System.out.println("Result set is scrollable");
else
    System.out.println("Result set is not scrollable");

二.物理分页
利用数据库本身的一些特性来分页。即:利用了数据库对sql语法的优化,提高分页性能。
1.针对Oracle数据库
步骤如下:
a.根据所使用的数据库特性来组织sql进行分页。
b.每次跳转页面的sql查询都不相同。
 通用的sql分页方式,“限制行数结果集的倒序”分页,步骤如下:
 (1).取得符合条件的所有结果集中可以唯一标识的Key值(通常是主键),并正向排序。
 (2).利用数据库提供的特殊方法进行“最大结果集”的限制(在Oracle中使用rownum, sql server中使用top, mysql中使用limit...),
 该“最大结果集”指包含当前所处页的所有记录数,“最大结果集”应该只包含惟一的Key值。
 (3).对步骤(2)中的“最大结果集”进行逆序,并取得“显示当前页显示数量的结果集”,该结果集中只包含惟一的Key值。
 (4).通过步骤(3)中所取得的Key值取得显示数据,该显示数据就是当前页应该显示的数据。

代码如下:

/** *//**

 * TestPageResultSetDAO.java

 *

 * Copyright 2008. All Rights Reserved.

 */

package com.cosmow.pageresultset.dao;

import java.sql.Connection;

import java.sql.DriverManager;

import java.sql.PreparedStatement;

import java.sql.ResultSet;

import java.sql.SQLException;

import java.util.ArrayList;

import java.util.List;

import com.cosmow.pageresultset.entity.Bars;

/** *//**

 * TODO dao class TestPageResultSetDAO

 * 

 * Revision History

 * 

 * 2008-7-7,Cosmo,created it

 */

public class TestPageResultSetDAO {

    //针对Oracle

    private final String FIND_BARS_ORACLE = "select b3.* from "

            + "(select b1.id from "

            + "(select b.id from yybars b where rownum <= ? order by b.id desc) b1 "

            + "where rownum <= ? order by b1.id desc) b2, "

            + "yybars b3 where b2.id = b3.id order by b2.id";

    /** *//**

     * 提供JDBC连接方法,返回一个Connection的实例

     * 

     * @return

     * @throws SQLException

     */

    private Connection getConnection() throws SQLException {

        try {

            final String url = "jdbc:oracle:thin:@localhost:1521:ORCL";

            final String user = "store";

            final String password = "store_password";

            Class.forName("oracle.jdbc.driver.OracleDriver");

            Connection con = DriverManager.getConnection(url, user, password);

            return con;

        } catch (ClassNotFoundException e) {

            throw new SQLException(e.getMessage());

        }

    }

    /** *//**

     * 物理分页方法一针对Oracle,使用sql语句的id查询来进行

     * 

     * @param currentPage

     *            当前页

     * @param showRows

     *            一页显示的数据量

     */

    public List<Bars> pageListThree(int currentPage, int showRows) {

        Connection con = null;

        PreparedStatement ps = null;

        ResultSet rs = null;

        ArrayList<Bars> resultList = new ArrayList<Bars>();

        try {

            con = getConnection();

            ps = con.prepareStatement(FIND_BARS_ORACLE);

            //传入参数,第一个参数标示包含该页总共有几条数据

            ps.setInt(1, showRows * currentPage);

            //第二个参数标示将取得在第一个参数所指定的数据中应该显示的数据

            ps.setInt(2, showRows);

            rs = ps.executeQuery();

            // 当返回结果集中有记录时

            while (rs.next()) {

                Bars bar = new Bars();

                bar.setId(rs.getLong("id"));

                bar.setName(rs.getString("name"));

                bar.setType(rs.getInt("type"));

                bar.setCreatorId(rs.getLong("creator_id"));

                resultList.add(bar);

            }

        } catch (SQLException e) {

            e.printStackTrace();

        } finally {

            try {

                if (rs != null)

                    rs.close();

                if (ps != null)

                    ps.close();

                if (con != null)

                    con.close();

            } catch (SQLException e) {

                e.printStackTrace();

            }

        }

        return resultList;

    }

}

2.针对MySQL数据库
在MySQL数据库中offset关键字的意思是"越过",而limit关键字的意思是“限制”,利用这两者结合可轻松分页。
(1)取得符合条件的结果集,包含全字段。
(2)利用offset关键字越过一段结果集(被越过的结果集就是"(当前页 - 1) * 一页显示数")。
(3)利用limit关键字限制取得一段结果集(被限制取得的结果集就是一页显示数)

代码如下:

/** *//**

 * TestPageResultSetDAO.java

 *

 * Copyright 2008. All Rights Reserved.

 */

package com.cosmow.pageresultset.dao;

import java.sql.Connection;

import java.sql.DriverManager;

import java.sql.PreparedStatement;

import java.sql.ResultSet;

import java.sql.SQLException;

import java.util.ArrayList;

import java.util.List;

import com.cosmow.pageresultset.entity.Bars;

/** *//**

 * TODO dao class TestPageResultSetDAO

 * 

 * Revision History

 * 

 * 2008-7-7,Cosmo,created it

 */

public class TestPageResultSetDAO {

    private final String FIND_BARS_MYSQL = "select * from yybars order by id limit ? offset ?";

    /** *//**

     * 提供JDBC连接方法,返回一个Connection的实例

     * 

     * @return

     * @throws SQLException

     */

    private Connection getConnection() throws SQLException {

        try {

            final String url = "jdbc:oracle:thin:@localhost:1521:ORCL";

            final String user = "store";

            final String password = "store_password";

            Class.forName("oracle.jdbc.driver.OracleDriver");

            Connection con = DriverManager.getConnection(url, user, password);

            return con;

        } catch (ClassNotFoundException e) {

            throw new SQLException(e.getMessage());

        }

    }

    /** *//**

     * 物理分页方法二针对mysql,使用sql语句的limit和offset来进行分页

     * 

     * @param currentPage

     *            当前页

     * @param showRows

     *            一页显示的数据量

     */

    public List<Bars> pageListFour(int currentPage, int showRows) {

        Connection con = null;

        PreparedStatement ps = null;

        ResultSet rs = null;

        ArrayList<Bars> resultList = new ArrayList<Bars>();

        try {

            con = getConnection();

            ps = con.prepareStatement(FIND_BARS_MYSQL);

            //传入参数,第一个参数表示显示几条记录(limit关键字的含义)

            ps.setInt(1, showRows);

            //第二个参数表示丢弃几条记录(offset关键字的含义)

            ps.setInt(2, showRows * (currentPage - 1));

            rs = ps.executeQuery();

            // 当返回结果集中有记录时

            while (rs.next()) {

                Bars bar = new Bars();

                bar.setId(rs.getLong("id"));

                bar.setName(rs.getString("name"));

                bar.setType(rs.getInt("type"));

                bar.setCreatorId(rs.getLong("creator_id"));

                resultList.add(bar);

            }

        } catch (SQLException e) {

            e.printStackTrace();

        } finally {

            try {

                if (rs != null)

                    rs.close();

                if (ps != null)

                    ps.close();

                if (con != null)

                    con.close();

            } catch (SQLException e) {

                e.printStackTrace();

            }

        }

        return resultList;

    }

}

分页结论:
1.物理分页速度上并不一定快于逻辑分页,逻辑分页速度上也并不一定快于物理分页。
2.物理分页总是优于逻辑分页:没有必要将属于数据库端的压力加诸到应用端来,就算速度上存在优势,
然而其它性能上的优点足以弥补这个缺点。
3.在分页工作前,有必要了解使用数据库本身的一些sql语句特点更好的分页。

源: http://www.cnblogs.com/wllyy189/articles/1237478.html

时间: 2024-10-08 09:46:45

几种分页方式分析的相关文章

SQL Server 中的三种分页方式

USE tempdb GO SET NOCOUNT ON --创建表结构 IF OBJECT_ID(N'ClassB', N'U') IS NOT NULL DROP TABLE ClassB GO CREATE TABLE ClassB(ID INT PRIMARY KEY, Name VARCHAR(16), CreateDate DATETIME, AID INT, Status INT) CREATE INDEX IDX_CreateDate ON ClassB(CreateDate)

Hibeernate中的两种分页方式

1. return getHibernateTemplate().executeFind(new HibernateCallback() { public Object doInHibernate(Session s) throws HibernateException { Criteria c = s.createCriteria(AskOnline.class); c.add(Restrictions.eq("boardid", new Long(bid))); c.setFirs

sqlserver的四种分页方式

第一种:ROW_NUMBER() OVER()方式 select * from ( select *, ROW_NUMBER() OVER(Order by ArtistId ) AS RowId from ArtistModels ) as b where RowId between 10 and 20 ---where RowId BETWEEN 当前页数-1*条数 and 页数*条数--- 执行结果是: 第二种方式:offset fetch next方式(SQL2012以上的版本才支持:推

SQL Server的三种分页方式

直接上代码 1 --top not in方式 2 select top 条数 * from tablename 3 where Id not in (select top 条数*页数 Id from tablename) 4 5 6 7 --ROW_NUMBER() OVER()方式 8 select * from ( 9 select *, ROW_NUMBER() OVER(Order by Id ) AS RowNumber from tablename 10 ) as b 11 wher

Asp.Net中的三种分页方式总结

本人ASP.net初学,网上找了一些分页的资料,看到这篇文章,没看到作者在名字,我转了你的文章,只为我可以用的时候方便查看,2010的文章了,不知道这技术是否过期. 以下才是正文 通常分页有3种方法,分别是asp.net自带的数据显示空间如GridView等自带的分页,第三方分页控件如aspnetpager,存储过程分页等.这里分别做总结. 第一种:使用GridView自带分页,这种是最简单的分页方法. 前台的方法: <asp:GridView ID="GridView1" Al

Elasticsearch 三种分页方式

from + size 浅分页 "浅"分页可以理解为简单意义上的分页.它的原理很简单,就是查询前20条数据,然后截断前10条,只返回10-20的数据.这样其实白白浪费了前10条的查询. GET test_dev/_search { "query": { "bool": { "filter": [ { "term": { "age": 28 } } ] } }, "size&qu

SQLServer2000到SQLServer2012三种分页方式比较

SQLSERVER2012 出新分页功能啦!!!近两天我在自己工作机的PC(没有并发,单一线程)上做了SqlServer  2000/ (2005/2008)/2012三个版本下的分页性能比较. 大致可得出以下结果:1.表数据量200W以内:SQLServer2012 的offset/fetch分页性能和SQLServer2005 Row_number的分页性能(仅考虑出结果速度)基本没区别(难分高下),略高于(大约10%)SQL2000的TOP分页性能.2.表数据量2000W左右:SQLSer

Sql Server 常见的几种分页方式

⒈offset fetch next方式[SqlServer2012及以上版本支持][推荐] 1 select * from T_User 2 order by id 3 offset 5 rows /*(页数-1) * 条数 */ 4 fetch next 5 rows only /* 条数 */ ⒉row_number() over()方式 1 select * from 2 (select *,row_number() over(order by id) as orderId from T

mybatis的两种分页方式:RowBounds和PageHelper

原理:拦截器. 使用方法: RowBounds:在mapper.java中的方法中传入RowBounds对象. RowBounds rowBounds = new RowBounds(offset, page.getPageSize()); // offset起始行 // limit是当前页显示多少条数据 public List<ProdProduct> findRecords(HashMap<String,Object> map,RowBounds rowBounds); map