这两天测试了前几天写的SQL2005专用分页的存储过程,当数据量达到2千多万的时候,效率相当的低,每次执行都要8秒左右(CPU:Q6600)。不过在2百多万数据量的情况下性能还是蛮不错的,在网上找了找,发现这下面的这两个,其实还是一个,不过后面那个是灵活了许多,仅供参考。
复制 保存/****** Object: StoredProcedure [dbo].[GetRecordFromPage] Script Date: 07/23/2008 18:42:05 ******/SET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGO/* 函数名称: GetRecordFromPage 函数功能: 获取指定页的数据 参数说明: @tblName 包含数据的表名 @fldName 关键字段名 @PageSize 每页记录数 @PageIndex 要获取的页码 @OrderType 排序类型, 0 - 升序, 1 - 降序 @strWhere 查询条件 (注意: 不要加 where) */ALTER PROCEDURE [dbo].[GetRecordFromPage] @tblName varchar(255), -- 表名 @fldName varchar(255), -- 字段名 @PageSize int = 10, -- 页尺寸 @PageIndex int = 1, -- 页码 @OrderType bit = 0, -- 设置排序类型, 非 0 值则降序 @strWhere varchar(2000) = '' -- 查询条件 (注意: 不要加 where)ASdeclare @strSQL varchar(6000) -- 主语句
declare @strTmp varchar(1000) -- 临时变量declare @strOrder varchar(500) -- 排序类型if @OrderType != 0
begin set @strTmp = '<(select min' set @strOrder = ' order by [' + @fldName +'] desc'endelsebegin set @strTmp = '>(select max' set @strOrder = ' order by [' + @fldName +'] asc'endset @strSQL = 'select top ' + str(@PageSize) + ' * from ['
+ @tblName + '] where [' + @fldName + ']' + @strTmp + '([' + @fldName + ']) from (select top ' + str((@PageIndex-1)*@PageSize) + ' [' + @fldName + '] from [' + @tblName + ']' + @strOrder + ') as tblTmp)' + @strOrderif @strWhere != ''
set @strSQL = 'select top ' + str(@PageSize) + ' * from [' + @tblName + '] where [' + @fldName + ']' + @strTmp + '([' + @fldName + ']) from (select top ' + str((@PageIndex-1)*@PageSize) + ' [' + @fldName + '] from [' + @tblName + '] where ' + @strWhere + ' ' + @strOrder + ') as tblTmp) and ' + @strWhere + ' ' + @strOrderif @PageIndex = 1
begin set @strTmp = '' if @strWhere != '' set @strTmp = ' where (' + @strWhere + ')'set @strSQL = 'select top ' + str(@PageSize) + ' * from ['
+ @tblName + ']' + @strTmp + ' ' + @strOrderendexec (@strSQL)
这是我目前见过效率最高的,不过它的order by 是不是有点儿问题,不能指定某一个,还有没有返回总记录数。于是有了下面这个改进的。如果需要总页数还可以再改一下,不过目前已经足够用了,当然,当达到千万数据量后,比上面的效率上稍微低了一点点,不过还不算太坏,
比较推荐下面这个。
复制 保存/****** Object: StoredProcedure [dbo].[usp_GetRecordFromPage] Script Date: 07/23/2008 18:42:37 ******/SET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE PROCEDURE [dbo].[usp_GetRecordFromPage] @tblName varchar(1000), -- 表名 @SelectFieldName varchar(4000), -- 要显示的字段名(不要加select) @strWhere varchar(4000), -- 查询条件(注意: 不要加 where) @OrderFieldName varchar(255), -- 排序索引字段名 @PageSize int , -- 页大小 @PageIndex int = 1, -- 页码 @iRowCount int output, -- 返回记录总数 @OrderType bit = 0 -- 设置排序类型, 非 0 值则降序ASdeclare @strSQL varchar(4000) -- 主语句
declare @strTmp varchar(4000) -- 临时变量declare @strOrder varchar(400) -- 排序类型declare @strRowCount nvarchar(4000) -- 用于查询记录总数的语句set @OrderFieldName=ltrim(rtrim(@OrderFieldName))
if @OrderType != 0
begin set @strTmp = '<(select min' set @strOrder = ' order by ' + @OrderFieldName +' desc'endelsebegin set @strTmp = '>(select max' set @strOrder = ' order by ' + @OrderFieldName +' asc'endset @strSQL = 'select top ' + str(@PageSize) + @SelectFieldName+' from '
+ @tblName + ' where ' + @OrderFieldName + @strTmp + '(' + right(@OrderFieldName,len(@OrderFieldName)-charindex('.',@OrderFieldName)) + ') from (select top ' + str((@PageIndex-1)*@PageSize) + @OrderFieldName + ' from ' + @tblName + @strOrder + ') as tblTmp)' + @strOrderif @strWhere != ''
set @strSQL = 'select top ' + str(@PageSize) + @SelectFieldName+' from ' + @tblName + ' where ' + @OrderFieldName + @strTmp + '(' + right(@OrderFieldName,len(@OrderFieldName)-charindex('.',@OrderFieldName)) + ') from (select top ' + str((@PageIndex-1)*@PageSize) + @OrderFieldName + ' from ' + @tblName + ' where ' + @strWhere + ' ' + @strOrder + ') as tblTmp) and ' + @strWhere + ' ' + @strOrderif @PageIndex = 1
begin set @strTmp = '' if @strWhere != '' set @strTmp = ' where ' + @strWhereset @strSQL = 'select top ' + str(@PageSize) + @SelectFieldName+' from '
+ @tblName + @strTmp + ' ' + @strOrderendexec(@strSQL)
if @strWhere!=''begin set @strRowCount = 'select @iRowCount=count(*) from ' + @tblName+' where endelsebegin set @strRowCount = 'select @iRowCount=count(*) from ' + @tblNameendexec sp_executesql @strRowCount,N'@iRowCount int out',@iRowCount out
在项目中,我们经常遇到或用到分页,那么在大数据量(百万级以上)下,哪种分页算法效率最优呢?我们不妨用事实说话。
测试环境硬件:CPU 酷睿双核T5750 内存:2G
软件:Windows server 2003 + Sql server 2005OK,我们首先创建一数据库:data_Test,并在此数据库中创建一表:tb_TestTable
复制 保存create database data_Test --创建数据库data_Test GO use data_Test GO create table tb_TestTable --创建表 ( id int identity(1,1) primary key, userName nvarchar(20) not null, userPWD nvarchar(20) not null, userEmail nvarchar(40) null ) GO然后我们在数据表中插入2000000条数据: 复制 保存--插入数据 set identity_insert tb_TestTable on declare @count int set @count=1 while @count<=2000000 begin insert into tb_TestTable(id,userName,userPWD,userEmail) values (@count,'admin','admin888','lli0077@yahoo.com.cn') set @count=@count+1 end set identity_insert tb_TestTable off我首先写了五个常用存储过程:
1,利用select top 和select not in进行分页,具体代码如下:
复制 保存create procedure proc_paged_with_notin --利用select top and select not in ( @pageIndex int, --页索引 @pageSize int --每页记录数 ) as begin set nocount on; declare @timediff datetime --耗时 declare @sql nvarchar(500) select @timediff=Getdate() 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' execute(@sql) --因select top后不支技直接接参数,所以写成了字符串@sql select datediff(ms,@timediff,GetDate()) as 耗时 set nocount off; end2,利用select top 和 select max(列键) 复制 保存create procedure proc_paged_with_selectMax --利用select top and select max(列) ( @pageIndex int, --页索引 @pageSize int --页记录数 ) as begin set nocount on; declare @timediff datetime declare @sql nvarchar(500) select @timediff=Getdate() 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' execute(@sql) select datediff(ms,@timediff,GetDate()) as 耗时 set nocount off; end3,利用select top和中间变量--此方法因网上有人说效果最佳,所以贴出来一同测试 复制 保存create procedure proc_paged_with_Midvar --利用ID>最大ID值和中间变量 ( @pageIndex int, @pageSize int ) as declare @count int declare @ID int declare @timediff datetime declare @sql nvarchar(500) begin set nocount on; select @count=0,@ID=0,@timediff=getdate() select @count=@count+1,@ID=case when @count< then ID else @ID end from tb_testTable order by id set @sql='select top '+str(@pageSize)+' * from tb_testTable where ID>'+str(@ID) execute(@sql) select datediff(ms,@timediff,getdate()) as 耗时 set nocount off; end4,利用Row_number() 此方法为SQL server 2005中新的方法,利用Row_number()给数据行加上索引 复制 保存create procedure proc_paged_with_Rownumber --利用SQL 2005中的Row_number() ( @pageIndex int, @pageSize int ) as declare @timediff datetime begin set nocount on; select @timediff=getdate() 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) select datediff(ms,@timediff,getdate()) as 耗时 set nocount off; end5,利用临时表及Row_number 复制 保存create procedure proc_CTE --利用临时表及Row_number ( @pageIndex int, --页索引 @pageSize int --页记录数 ) as set nocount on; declare @ctestr nvarchar(400) declare @strSql nvarchar(400) declare @datediff datetime begin select @datediff=GetDate() set @ctestr='with Table_CTE as (select ceiling((Row_number() over(order by ID ASC))/'+str(@pageSize)+') as page_num,* from tb_TestTable)'; set @strSql=@ctestr+' select * From Table_CTE where page_num='+str(@pageIndex) end begin execute sp_executesql @strSql select datediff(ms,@datediff,GetDate()) set nocount off; endOK,至此,存储过程创建完毕,我们分别在每页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 5临时表 780ms 796ms 798ms 780ms 805ms 4