在项目中,我们经常遇到或用到分页,那么在大数据量(百万级以上)下,哪种分页算法效率最优呢?我们不妨用事实说话。

 

测试环境

硬件:CPU 酷睿双核T5750  内存:2G

软件:Windows server 2003    +   Sql server 2005

 

OK,我们首先创建一数据库:data_Test,并在此数据库中创建一表:tb_TestTable

 


 1各种分页存储过程  (转)create database data_Test  --创建数据库data_Test 
 2各种分页存储过程  (转)GO 
 3各种分页存储过程  (转)use data_Test 
 4各种分页存储过程  (转)GO 
 5各种分页存储过程  (转)create table tb_TestTable   --创建表 
 6各种分页存储过程  (转)
 7各种分页存储过程  (转)    id int identity(1,1) primary key, 
 8各种分页存储过程  (转)    userName nvarchar(20) not null, 
 9各种分页存储过程  (转)    userPWD nvarchar(20) not null, 
10各种分页存储过程  (转)    userEmail nvarchar(40) null 
11各种分页存储过程  (转)
12各种分页存储过程  (转)GO

 

然后我们在数据表中插入2000000条数据:

 


 1各种分页存储过程  (转)--插入数据 
 2各种分页存储过程  (转)set identity_insert tb_TestTable on 
 3各种分页存储过程  (转)declare @count int 
 4各种分页存储过程  (转)set @count=1 
 5各种分页存储过程  (转)while @count<=2000000 
 6各种分页存储过程  (转)begin  
 7各种分页存储过程  (转)    insert into tb_TestTable(id,userName,userPWD,userEmail) values(@count,'admin','admin888','lli0077@yahoo.com.cn') 
 8各种分页存储过程  (转)    set @count=@count+1 
 9各种分页存储过程  (转)end 
10各种分页存储过程  (转)set identity_insert tb_TestTable off

 

我首先写了五个常用存储过程:

1,利用select top 和select not in进行分页,具体代码如下:

 


 1各种分页存储过程  (转)create procedure proc_paged_with_notin  --利用select top and select not in 
 2各种分页存储过程  (转)
 3各种分页存储过程  (转)    @pageIndex int,  --页索引 
 4各种分页存储过程  (转)    @pageSize int    --每页记录数 
 5各种分页存储过程  (转)
 6各种分页存储过程  (转)as 
 7各种分页存储过程  (转)begin 
 8各种分页存储过程  (转)    set nocount on; 
 9各种分页存储过程  (转)    declare @timediff datetime --耗时 
10各种分页存储过程  (转)    declare @sql nvarchar(500) 
11各种分页存储过程  (转)    select @timediff=Getdate() 
12各种分页存储过程  (转)    set @sql='select top '+str(@pageSize)+' * from tb_TestTable where(ID not in(select top '+str(@pageSize*@pageIndex)+' id from tb_TestTable order by ID ASC)) order by ID' 
13各种分页存储过程  (转)    execute(@sql)  --因select top后不支技直接接参数,所以写成了字符串@sql 
14各种分页存储过程  (转)    select datediff(ms,@timediff,GetDate()) as 耗时 
15各种分页存储过程  (转)    set nocount off; 
16各种分页存储过程  (转)end

 

2,利用select top 和 select max(列键)

 

 


 1各种分页存储过程  (转)create procedure proc_paged_with_selectMax  --利用select top and select max(列) 
 2各种分页存储过程  (转)
 3各种分页存储过程  (转)    @pageIndex int,  --页索引 
 4各种分页存储过程  (转)    @pageSize int    --页记录数 
 5各种分页存储过程  (转)
 6各种分页存储过程  (转)as 
 7各种分页存储过程  (转)begin 
 8各种分页存储过程  (转)set nocount on; 
 9各种分页存储过程  (转)    declare @timediff datetime 
10各种分页存储过程  (转)    declare @sql nvarchar(500) 
11各种分页存储过程  (转)    select @timediff=Getdate() 
12各种分页存储过程  (转)    set @sql='select top '+str(@pageSize)+' * From tb_TestTable where(ID>(select max(id) From (select top '+str(@pageSize*@pageIndex)+' id From tb_TestTable order by ID) as TempTable)) order by ID' 
13各种分页存储过程  (转)    execute(@sql) 
14各种分页存储过程  (转)    select datediff(ms,@timediff,GetDate()) as 耗时 
15各种分页存储过程  (转)set nocount off; 
16各种分页存储过程  (转)end

 

3,利用select top和中间变量--此方法因网上有人说效果最佳,所以贴出来一同测试


 


 1各种分页存储过程  (转)create procedure proc_paged_with_Midvar  --利用ID>最大ID值和中间变量 
 2各种分页存储过程  (转)
 3各种分页存储过程  (转)    @pageIndex int, 
 4各种分页存储过程  (转)    @pageSize int 
 5各种分页存储过程  (转)
 6各种分页存储过程  (转)as 
 7各种分页存储过程  (转)    declare @count int 
 8各种分页存储过程  (转)    declare @ID int 
 9各种分页存储过程  (转)    declare @timediff datetime 
10各种分页存储过程  (转)    declare @sql nvarchar(500) 
11各种分页存储过程  (转)begin 
12各种分页存储过程  (转)set nocount on; 
13各种分页存储过程  (转)    select @count=0,@ID=0,@timediff=getdate() 
14各种分页存储过程  (转)    select @count=@count+1,@ID=case when @count<=@pageSize*@pageIndex then ID else @ID end from tb_testTable order by id 
15各种分页存储过程  (转)    set @sql='select top '+str(@pageSize)+' * from tb_testTable where ID>'+str(@ID) 
16各种分页存储过程  (转)    execute(@sql) 
17各种分页存储过程  (转)    select datediff(ms,@timediff,getdate()) as 耗时 
18各种分页存储过程  (转)set nocount off; 
19各种分页存储过程  (转)end
20各种分页存储过程  (转)

 

4,利用Row_number() 此方法为SQL server 2005中新的方法,利用Row_number()给数据行加上索引

 


 


 1各种分页存储过程  (转)create procedure proc_paged_with_Rownumber  --利用SQL 2005中的Row_number() 
 2各种分页存储过程  (转)
 3各种分页存储过程  (转)    @pageIndex int, 
 4各种分页存储过程  (转)    @pageSize int 
 5各种分页存储过程  (转)
 6各种分页存储过程  (转)as 
 7各种分页存储过程  (转)    declare @timediff datetime 
 8各种分页存储过程  (转)begin 
 9各种分页存储过程  (转)set nocount on; 
10各种分页存储过程  (转)    select @timediff=getdate() 
11各种分页存储过程  (转)    select * from (select *,Row_number() over(order by ID asc) as IDRank from tb_testTable) as IDWithRowNumber where IDRank>@pageSize*@pageIndex and IDRank<@pageSize*(@pageIndex+1) 
12各种分页存储过程  (转)    select datediff(ms,@timediff,getdate()) as 耗时 
13各种分页存储过程  (转)set nocount off; 
14各种分页存储过程  (转)end
15各种分页存储过程  (转)

5,利用临时表及Row_number


 


 1各种分页存储过程  (转)create procedure proc_CTE  --利用临时表及Row_number 
 2各种分页存储过程  (转)
 3各种分页存储过程  (转)    @pageIndex int,  --页索引 
 4各种分页存储过程  (转)    @pageSize int    --页记录数 
 5各种分页存储过程  (转)
 6各种分页存储过程  (转)as 
 7各种分页存储过程  (转)    set nocount on; 
 8各种分页存储过程  (转)    declare @ctestr nvarchar(400) 
 9各种分页存储过程  (转)    declare @strSql nvarchar(400) 
10各种分页存储过程  (转)    declare @datediff datetime 
11各种分页存储过程  (转)begin 
12各种分页存储过程  (转)    select @datediff=GetDate() 
13各种分页存储过程  (转)    set @ctestr='with Table_CTE as 
14各种分页存储过程  (转)                (select ceiling((Row_number() over(order by ID ASC))/'+str(@pageSize)+') as page_num,* from tb_TestTable)'; 
15各种分页存储过程  (转)    set @strSql=@ctestr+' select * From Table_CTE where page_num='+str(@pageIndex) 
16各种分页存储过程  (转)end 
17各种分页存储过程  (转)    begin 
18各种分页存储过程  (转)        execute sp_executesql @strSql 
19各种分页存储过程  (转)        select datediff(ms,@datediff,GetDate()) 
20各种分页存储过程  (转)    set nocount off; 
21各种分页存储过程  (转)    end
22各种分页存储过程  (转)

 

OK,至此,存储过程创建完毕,我们分别在每页10条数据的情况下在第2页,第1000页,第10000页,第100000页,第199999页进行测试,耗时单位:ms  每页测试5次取其平均值

 

存过 第2页耗时 第1000页耗时 第10000页耗时 第100000页耗时 第199999页耗时 效率排行
1用not in 0ms 16ms 47ms 475ms 953ms 3
2用select max 5ms 16ms 35ms 325ms 623ms 1
3中间变量 966ms 970ms 960ms 945ms 933ms 5
4row_number 0ms 0ms 34ms 365ms 710ms 2
4临时表 780ms 796ms 798ms 780ms 805ms 4

 

 

测试结果显示:select max >row_number>not in>临时表>中间变量

 

于是我对效率最高的select max方法用2分法进行了扩展,代码取自互联网,我修改了ASC排序时取不到值的BUG,测试结果:

 

2分法 156ms 156ms 180ms 470ms 156ms 1*

 

 

从测试结果来看,使用2分法确实可以提高效率并使效率更为稳定,我又增加了第159999页的测试,用时仅296ms,效果相当的不错!

 

下面是2分法使用select max的代码,已相当完善。


 


  1各种分页存储过程  (转)--/*-----存储过程 分页处理 孙伟 2005-03-28创建 -------*/ 
  2各种分页存储过程  (转)--/*-----存储过程 分页处理 浪尘 2008-9-1修改----------*/ 
  3各种分页存储过程  (转)--/*----- 对数据进行了2分处理使查询前半部分数据与查询后半部分数据性能相同 -------*/ 
  4各种分页存储过程  (转)
  5各种分页存储过程  (转)alter PROCEDURE proc_paged_2part_selectMax 
  6各种分页存储过程  (转)
  7各种分页存储过程  (转)@tblName     nvarchar(200),        ----要显示的表或多个表的连接 
  8各种分页存储过程  (转)@fldName     nvarchar(500) = '*',    ----要显示的字段列表 
  9各种分页存储过程  (转)@pageSize    int = 10,        ----每页显示的记录个数 
 10各种分页存储过程  (转)@page        int = 1,        ----要显示那一页的记录 
 11各种分页存储过程  (转)@fldSort    nvarchar(200) = null,    ----排序字段列表或条件 
 12各种分页存储过程  (转)@Sort        bit = 0,        ----排序方法,0为升序,1为降序(如果是多字段排列Sort指代最后一个排序字段的排列顺序(最后一个排序字段不加排序标记)--程序传参如:' SortA Asc,SortB Desc,SortC ') 
 13各种分页存储过程  (转)@strCondition    nvarchar(1000) = null,    ----查询条件,不需where 
 14各种分页存储过程  (转)@ID        nvarchar(150),        ----主表的主键 
 15各种分页存储过程  (转)@Dist                 bit = 0,           ----是否添加查询字段的 DISTINCT 默认0不添加/1添加 
 16各种分页存储过程  (转)@pageCount    int = 1 output,            ----查询结果分页后的总页数 
 17各种分页存储过程  (转)@Counts    int = 1 output                ----查询到的记录数 
 18各种分页存储过程  (转)
 19各种分页存储过程  (转)AS 
 20各种分页存储过程  (转)SET NOCOUNT ON 
 21各种分页存储过程  (转)Declare @sqlTmp nvarchar(1000)        ----存放动态生成的SQL语句 
 22各种分页存储过程  (转)Declare @strTmp nvarchar(1000)        ----存放取得查询结果总数的查询语句 
 23各种分页存储过程  (转)Declare @strID     nvarchar(1000)        ----存放取得查询开头或结尾ID的查询语句 
 24各种分页存储过程  (转)
 25各种分页存储过程  (转)Declare @strSortType nvarchar(10)    ----数据排序规则A 
 26各种分页存储过程  (转)Declare @strFSortType nvarchar(10)    ----数据排序规则B 
 27各种分页存储过程  (转)
 28各种分页存储过程  (转)Declare @SqlSelect nvarchar(50)         ----对含有DISTINCT的查询进行SQL构造 
 29各种分页存储过程  (转)Declare @SqlCounts nvarchar(50)          ----对含有DISTINCT的总数查询进行SQL构造 
 30各种分页存储过程  (转)
 31各种分页存储过程  (转)declare @timediff datetime  --耗时测试时间差 
 32各种分页存储过程  (转)select @timediff=getdate() 
 33各种分页存储过程  (转)
 34各种分页存储过程  (转)if @Dist  = 0 
 35各种分页存储过程  (转)begin 
 36各种分页存储过程  (转)    set @SqlSelect = 'select ' 
 37各种分页存储过程  (转)    set @SqlCounts = 'Count(*)' 
 38各种分页存储过程  (转)end 
 39各种分页存储过程  (转)else 
 40各种分页存储过程  (转)begin 
 41各种分页存储过程  (转)    set @SqlSelect = 'select distinct ' 
 42各种分页存储过程  (转)    set @SqlCounts = 'Count(DISTINCT '+@ID+')' 
 43各种分页存储过程  (转)end 
 44各种分页存储过程  (转)
 45各种分页存储过程  (转)
 46各种分页存储过程  (转)if @Sort=0 
 47各种分页存储过程  (转)begin 
 48各种分页存储过程  (转)    set @strFSortType=' ASC ' 
 49各种分页存储过程  (转)    set @strSortType=' DESC ' 
 50各种分页存储过程  (转)end 
 51各种分页存储过程  (转)else 
 52各种分页存储过程  (转)begin 
 53各种分页存储过程  (转)    set @strFSortType=' DESC ' 
 54各种分页存储过程  (转)    set @strSortType=' ASC ' 
 55各种分页存储过程  (转)end 
 56各种分页存储过程  (转)
 57各种分页存储过程  (转)
 58各种分页存储过程  (转)
 59各种分页存储过程  (转)--------生成查询语句-------- 
 60各种分页存储过程  (转)--此处@strTmp为取得查询结果数量的语句 
 61各种分页存储过程  (转)if @strCondition is null or @strCondition=''     --没有设置显示条件 
 62各种分页存储过程  (转)begin 
 63各种分页存储过程  (转)    set @sqlTmp =  @fldName + ' From ' + @tblName 
 64各种分页存储过程  (转)    set @strTmp = @SqlSelect+' @Counts='+@SqlCounts+' FROM '+@tblName 
 65各种分页存储过程  (转)    set @strID = ' From ' + @tblName 
 66各种分页存储过程  (转)end 
 67各种分页存储过程  (转)else 
 68各种分页存储过程  (转)begin 
 69各种分页存储过程  (转)    set @sqlTmp = + @fldName + 'From ' + @tblName + ' where (1>0) ' + @strCondition 
 70各种分页存储过程  (转)    set @strTmp = @SqlSelect+' @Counts='+@SqlCounts+' FROM '+@tblName + ' where (1>0) ' + @strCondition 
 71各种分页存储过程  (转)    set @strID = ' From ' + @tblName + ' where (1>0) ' + @strCondition 
 72各种分页存储过程  (转)end 
 73各种分页存储过程  (转)
 74各种分页存储过程  (转)----取得查询结果总数量----- 
 75各种分页存储过程  (转)exec sp_executesql @strTmp,N'@Counts int out ',@Counts out 
 76各种分页存储过程  (转)declare @tmpCounts int 
 77各种分页存储过程  (转)if @Counts = 0 
 78各种分页存储过程  (转)    set @tmpCounts = 1 
 79各种分页存储过程  (转)else 
 80各种分页存储过程  (转)    set @tmpCounts = @Counts 
 81各种分页存储过程  (转)
 82各种分页存储过程  (转)    --取得分页总数 
 83各种分页存储过程  (转)    set @pageCount=(@tmpCounts+@pageSize-1)/@pageSize 
 84各种分页存储过程  (转)
 85 

执行示例:exec proc_paged_2part_selectMax 'tb_testTable','ID,userName,userPWD,userEmail',10,100000,'ID',0,null,'ID',0

 

这种测试只在单机进行,并且没有在实际开发WEB项目中分页测试,测试项也比较单一,所以不够全面系统,但从其效率相比上,我们可以在数据库分页算法上进行有效的控制。

相关文章: