用DataReader 分页与几种传统的分页方法的比较

本文介绍使用DataReader实现数据库分页的方法,并对比了几种传统分页技术,包括二次Top、ROW_NUMBER及通用分页存储过程。测试结果显示DataReader在不同分页情况下的效率表现。

用DataReader 分页与几种传统的分页方法的比较

作者:肖波

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

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


<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> --> 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 分页的方法:

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


<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> --> public DataSetRangeQuery( string queryString, long first, long last)
ExpandedBlockStart.gifContractedBlock.gif
{
try
ExpandedSubBlockStart.gifContractedSubBlock.gif
{
OpenDataReader(queryString);

if(first<0)
ExpandedSubBlockStart.gifContractedSubBlock.gif
{
first
=0;
}


for(longi=0;i<first;i++)
ExpandedSubBlockStart.gifContractedSubBlock.gif
{
if(!_DataReader.Read())
ExpandedSubBlockStart.gifContractedSubBlock.gif
{
return_SchemaDataSet;
}

}


if(last<0)
ExpandedSubBlockStart.gifContractedSubBlock.gif
{
last
=0x7FFFFFFFFFFFFFFF;
}


for(longi=first;i<=last;i++)
ExpandedSubBlockStart.gifContractedSubBlock.gif
{
DataRowrow
=NextRow();

if(row!=null)
ExpandedSubBlockStart.gifContractedSubBlock.gif
{
_SchemaTable.Rows.Add(row);
}

else
ExpandedSubBlockStart.gifContractedSubBlock.gif
{
return_SchemaDataSet;
}

}


return_SchemaDataSet;
}

finally
ExpandedSubBlockStart.gifContractedSubBlock.gif
{
CloseDataReader();
}

}

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

数据,所以效率很高。

几种常用方法介绍

1. 二次 TOP

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

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> --> GO
if exists ( select * from sysobjects where id = object_id ( ' PagedProc ' ) and type = ' p ' )
drop procedure PagedProc
GO
create procedure PagedProc
@currentpage int , -- pageno
@pagesize int -- pagesize
as
declare
@sqlstr nvarchar ( 4000 ) -- Querystring

if @currentpage = 1
begin
set @sqlstr = ' SELECTTOP ' + Str ( @pagesize ) + ' *fromr_studentorderbyId '
end
else
begin

set @sqlstr = ' SELECTTOP ' + Str ( @pagesize ) + ' *fromr_studentwhereidnotin ' ;
set @sqlstr = @sqlstr + ' (SELECTTOP ' + Str (( @currentpage - 1 ) * @pagesize ) + ' idfromr_studentorderbyId) '

end

exec ( @sqlstr )

GO

2. ROWNUMBER

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

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

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->
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 , -- pageno
@pagesize int -- pagesize
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

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

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

还是可以的。

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> --> 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:<张婷婷>
--
Createdate:<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 )

-- AlterByTracy.Chuang2006-08-21更改分页算法,采用比较最大值的方法
Set @Sql =
' SelectTop ' + Cast ( @PageSize As
nVarchar ( 10 )) + ' ' + @Select + ' From ' + @From + ' Where ' + Case
IsNull ( @Where , '' ) When '' Then '' Else @Where + ' And ' End +
@Key + ' >(SelectISNULL(MAX( ' + @Key + ' ),0)ASMaxID
From(SelectTop
' + Cast ( @PageSize * ( @Page - 1 ) As Varchar ( 10 )) + '
' + @Key +
' From ' + @From +
Case IsNull ( @Where , '' ) When '' Then '' Else ' Where ' + @Where End +
' OrderBy ' + @Key + ' )AsT) ' +
' OrderBy ' + @Key + Case IsNull ( @OrderBy , '' ) When '' Then '' Else
' , ' + @OrderBy End

Exec ( @Sql )

END

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

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

分页方法第1页第10页第100页第1000页第10000页第100000页
二次 Top47404282713926
ROW_NUMBER112121083594
通用分页11110823487
DataReader0019913380


源码下载位置

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值