对于数据库的分页,目前比较传统的方法是采用分页存储过程,其实用 DataReader 也可以实现分页,不需要写存储过程,实现效率上也比几种比较流行的分页方法要略快。

在开始这个方法之前,让我们先创建一个简单的测试环境:


use Test
GO if exists (select * from sysobjects where id = object_id('R_Student') and type = 'u')
    drop table R_Student
GO
create table R_Student
(
    Id          nvarchar(64)  Primary Key,
    Class       nvarchar(64)  NOT NULL,
    Age         tinyint       NOT NULL,
    Sex         tinyint       NOT NULL    
) GO
Declare
@i int
set @i = 0;
while (@i < 1000000)
begin
insert R_Student values('Name' + Str(@i),'Class' + Str(@i), @i % 100, @i % 2)
set @i = @i + 1
end

通过上述语句创建一个简单的数据表,并插入100万条记录

DataReader 分页的方法:

说出来很简单,见下面程序   源码下载位置


        public DataSet RangeQuery(string queryString, long first, long last)
        {
            try
            {
                OpenDataReader(queryString);                 if (first < 0)
                {
                    first = 0;
                }                 for (long i = 0; i < first; i++)
                {
                    if (!_DataReader.Read())
                    {
                        return _SchemaDataSet;
                    }
                }                 if (last < 0)
                {
                    last = 0x7FFFFFFFFFFFFFFF;
                }                 for (long i = first; i <= last; i++)
                {
                    DataRow row = NextRow();                     if (row != null)
                    {
                        _SchemaTable.Rows.Add(row);
                    }
                    else
                    {
                        return _SchemaDataSet;
                    }
                }                 return _SchemaDataSet;
            }
            finally
            {
                CloseDataReader();
            }
        }

其实就是通过DataReader 将当前记录移动到起始页对应的那条纪录,然后再开始读数据。由于之前只是移动记录指针,并不读取

数据,所以效率很高。

几种常用方法介绍

1. 二次 TOP

这种方法效率较低,问题主要处在那个 not in 上面,另外如果Id 是可重复的,得出的结果是


GO
if exists (select * from sysobjects where id = object_id('PagedProc') and type = 'p')
    drop procedure PagedProc
GO
create procedure PagedProc
@currentpage int, -- page no
@pagesize int --page size
as
declare
@sqlstr nvarchar(4000) --Query string if @currentpage = 1 
begin
set @sqlstr = 'SELECT TOP ' + Str(@pagesize) + '* from r_student order by Id'
end
else
begin set @sqlstr = 'SELECT TOP ' + Str(@pagesize) + ' * from r_student where id not in';
set @sqlstr = @sqlstr + '(SELECT TOP '+ Str((@currentpage-1)*@pagesize) + ' id from  r_student order by Id)' end exec (@sqlstr) GO

2. ROWNUMBER

这个方法不受排序字段,以及重复键等的约束,非常通用。效率也不错。说白了,就是先将查询结果存到临时表中,

并为这个临时表提供一个自增长的索引字段,然后根据这个字段进行查询范围。


if exists (select * from sysobjects where id = object_id('PagedProcUseROW_NUMBER') and type = 'p')
    drop procedure PagedProcUseROW_NUMBER
GO
create procedure PagedProcUseROW_NUMBER
@currentpage int, -- page no
@pagesize int --page size
as
begin
WITH student AS
(
    SELECT *,
    ROW_NUMBER() OVER (ORDER BY Id) AS 'RowNumber'
    FROM r_student 

SELECT * 
FROM student 
WHERE RowNumber BETWEEN (@currentpage-1)*@pagesize + 1 AND (@currentpage)*@pagesize;
end
GO

3. 通用分页存储过程

这个存储过程的出处:

http://www.cnblogs.com/Tracy-Chuang/archive/2006/10/16/530125.html

我稍微改了一点,去掉了一些功能,方便测试。

这个存储过程有一些缺点,比如不支持多字段主键,重复键的处理看似也有问题,不排序也不可以。单纯从效率看,

还是可以的。


if exists (select * from sysobjects where id = object_id('[spCommonPageData]') and type = 'p')
    drop procedure [spCommonPageData]
GO
--http://www.cnblogs.com/Tracy-Chuang/archive/2006/10/16/530125.html -- =============================================
-- Author:  <张婷婷>
-- Create date: <2006-08-24>
-- Description: <通用分页存储过程>
-- =============================================
Create PROCEDURE [dbo].[spCommonPageData]
 @Select NVARCHAR(500),   -- 要查询的列名,用逗号隔开(Select后面From前面的内容)
 @From NVARCHAR(200),   -- From后的内容
 @Where NVARCHAR(500) = NULL, -- Where后的内容
 @OrderBy NVARCHAR(100) = NULL, -- 排序字段
 @Key NVARCHAR(50),    -- 分页主键
 @Page INT,      -- 当前页 ***计数从1开始***
 @PageSize INT     -- 每页大小
AS
BEGIN
 SET NOCOUNT ON;  Declare @Sql nVarchar(1000), @Sql2 NVARCHAR(500) --Alter By Tracy.Chuang 2006-08-21更改分页算法,采用比较最大值的方法
 Set @Sql=
 'Select Top '+Cast(@PageSize As
 nVarchar(10))+' '+@Select+  ' From '+@From+  ' Where '+Case
 IsNull(@Where,'') When '' Then '' Else @Where+' And ' End+
 @Key+' >( Select ISNULL(MAX('+@Key+'), 0) AS MaxID
    From (Select Top '+Cast(@PageSize*(@Page-1) As Varchar(10))+' 
 '+@Key+
       ' From '+@From+
       Case IsNull(@Where,'') When '' Then '' Else ' Where '+@Where End+
       ' Order By '+@Key+') As T)'+
 ' Order By '+@Key+Case IsNull(@OrderBy,'') When '' Then '' Else
 ','+@OrderBy End  Exec(@Sql)
 
END

四种方法的效率比较。只做了一种条件下测试,其他条件大家有兴趣可以自己测。

PageSize = 10, 记录总数 100万,时间单位为毫秒

分页方法 第1页 第10页 第100页 第1000页 第10000页 第100000页
二次 Top 4 7 404 28 271 3926
ROW_NUMBER 1 1 2 12 108 3594
通用分页 1 1 1 10 82 3487
DataReader 0 0 1 9 91 3380

用DataReader 分页与几种传统的分页方法的比较的更多相关文章

  1. 从官方文档中探索MySQL分页的几种方式及分页优化

    概览 相比于Oracle,SQL Server 等数据库,MySQL分页的方式简单得多了,官方自带了分页语法 limit 语句: select * from test_t LIMIT {[offset ...

  2. MongoDB实现分页(两种方法)

    1.插入实验数据 偷懒用下samus,100条. ; i < ; i++) { Document doc = new Document(); doc["ID"] = i; d ...

  3. [BS-28] iOS中分页的几种算法

    iOS中分页的几种算法 总记录数:totalRecord 每页最大记录数:maxResult 算法一: totalPage = totalRecord % maxResult == 0 ? total ...

  4. 几种常见SQL分页方式效率比较(转)

    http://www.cnblogs.com/iamowen/archive/2011/11/03/2235068.html 分页很重要,面试会遇到.不妨再回顾总结一下. 1.创建测试环境,(插入10 ...

  5. 查询分页的几种Sql写法

    查询分页的几种Sql写法 摘自:http://www.cnblogs.com/zcttxs/archive/2012/04/01/2429151.html 1.创建测试环境,(插入100万条数据大概耗 ...

  6. 盘点几种数据库的分页SQL的写法(转)

    Data序列——盘点几种数据库的分页SQL的写法http://www.cnblogs.com/fireasy/archive/2013/04/10/3013088.html

  7. 3种SQL语句分页写法

    在开发中经常会使用到数据分页查询,一般的分页可以直接用SQL语句分页,当然也可以把分页写在存储过程里,下面是三种比较常用的SQL语句分页方法,下面以每页5条数据,查询第3页为例子: 第一种:使用not ...

  8. MySQL、SQLServer2000(及SQLServer2005)和ORCALE三种数据库实现分页查询的方法

    在这里主要讲解一下MySQL.SQLServer2000(及SQLServer2005)和ORCALE三种数据库实现分页查询的方法. 可能会有人说这些网上都有,但我的主要目的是把这些知识通过我实际的应 ...

  9. 浅谈js分页的几种方法

    一个项目中必然会遇到分页这种需求的,分页可以使数据加载更合理,也让页面显示更美观,更有层次感!那么js分页到底如何实现呢?下面我就来讲一下三种循序渐进的方法 1.自己纯手写分页 更深入的去理解分页的意 ...

随机推荐

  1. 手动脱RLPack壳实战

    作者:Fly2015 吾爱破解论坛培训第一课选修作业练习的第7题. 这个壳没听说过.可是脱起来比較简单.依据ESP定律就可以直达光明,Dump出原来的程序. 老规矩.首先对须要脱壳的程序进行查壳处理. ...

  2. mysql查看所有存储过程,函数,视图,触发器,表

    查询数据库中的存储过程和函数 方法一: select `name` from mysql.proc where db = 'your_db_name' and `type` = 'PROCEDURE' ...

  3. ExtJS ComboBox 下拉列表详细用法

    ExtJS ComboBox 下拉列表详细用法 标签: combobox 2015-06-14 23:23 5171人阅读 评论(2) 收藏 举报  分类: ExtJS(32)    目录(?)[+] ...

  4. Cocos2D-X2.2.3学习笔记12(瞬时动作)

    watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaHVjYmxvZw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA ...

  5. vs2012下安装Cocos2d-x模板问题

    今天想開始学Cocos2d-x.于是依据书本的提示到网上去下载了所需的安装包.我下载的cocos2d-x版本号是2.2.3.在下载完毕之后依照书中的步骤对其环境进行配置.在搞到模板安装这一步,发现找不 ...

  6. Python开发【第*篇】【Socket网络编程】

    1.Socket socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求. so ...

  7. frame pointer及其用途

    1 什么是frame pointer frame pointer指向本函数栈帧顶,通过它可以找到本函数在进程栈中的位置.有专门的寄存器保存该值. 2 frame pointer有什么用 主要是back ...

  8. Delphi中SendMessage使用说明(所有消息说明) good

    Delphi中SendMessage使用说明 SendMessage基础知识 函数功能:该函数将指定的消息发送到一个或多个窗口.此函数为指定的窗口调用窗口程序,直到窗口程序处理完消息再返回.而函数Po ...

  9. APDU报文【转】

    本文转载自:http://www.cnbolgs.com/snail0404/p/5436348.html APDU   # APDU # 定义:APDU(ApplicationProtocolDat ...

  10. Java 类型信息 —— 获取泛型类型的类对象(.class)

    How to get a class instance of generics type T 考虑泛型类Foo<T>,在其成员中,如果想获取类型(type)T的类实例(class inst ...