对于数据库的分页,目前比较传统的方法是采用分页存储过程,其实用 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. 离线安装Cloudera Manager5.3.4与CDH5.3.4

    文章转载:http://www.aboutyun.com/thread-14024-1-1.html 前期准备工作(系统环境搭建) 操作系统:CentOS 6.5 x64 CPU*2 64G 300G ...

  2. 运行shell脚本报错 &#39;\357\273\277&#39;: command not found 解决的方法

    1,删除BOM,在vi以下运行以下的命令就可以 :set nobomb 2,原因: 所谓BOM,全称是Byte Order Mark.它是一个Unicode字符,通常出如今文本的开头,用来标识字节序( ...

  3. swift 笔记 (二十一) —— 高级运算符

    高级运算符 位运算符 按位取反: ~ 按位与运算:  & 按位或运算:  | 按位异或运算:  ^ 按位左移运算: << 按位右移动算: >> 溢出运算符 自从swif ...

  4. build.gradle解析

    1 providedCompile.testImplementation.implementation.api.runtime.testCompile.classpath.natives 这些都是和p ...

  5. Aaron Swartz Rewriting Reddit中关于web.py的创建思路

    这天才少年居然自杀了,哎 原文点这 So how should things work? The first principle is that code should be clear and si ...

  6. git ignore的一些技巧

    当想要ignore的部分已经纳入版本控制的时候,可以使用 git rm --cache -rf cache 来强制ignore

  7. 8-23 canvas专题

    8-23 canvas专题-了解外部框架的使用 学习要点 掌握画布内容的导出的toDataURL()方法 了解外部框架的使用 第八章内容介绍 在第八章中我们将对以前的知识进行简单的回顾,着重对canv ...

  8. ZOJ1450 Minimal Circle 最小圆覆盖

    ZOJ1450 给定N个点(N<=100)求最小的圆把这些点全部覆盖 考虑对于三角形,可以唯一的找到外接圆,而多边形又可以分解为三角形,所以对于多边形也可以找到唯一的最小覆盖圆. #includ ...

  9. bzoj3786

    splay维护dfs序 我们发现有移动子树这种操作,树剖是做不了了,又要实现子树加,lct又维护不了了,这时我们用splay维护入栈出栈序来支持这些操作.我们记录每个点的入栈时间和出栈时间,这样一个闭 ...

  10. cURL模拟HTTP请求(支持HTTPS)

    function setHttpRequest($url,$headers,$params=array(),$method="GET") { $ci = curl_init(); ...