--分頁性能測試
--1 建立測試表
CREATE TABLE [dbo].[tb_group](
[ID] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](50) NULL,
[city] [nvarchar](50) NULL,
[province] [nvarchar](50) NULL,
[country] [nvarchar](50) NULL,
CONSTRAINT [PK_tb_group] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
--2 插入300萬條記錄
select @@version
declare @count int
declare @ch char(4)
declare @c nvarchar(2)
declare @i int
set @count=3000000
while @count>0
begin
set @i=65+floor(rand()*26)
set @c=convert(nvarchar(2),@i)
set @ch=replicate(char(@i),4)
insert tb_group ([name],[city],[province],[country]) values
(@ch,''C''+@c,''P''+@c,@ch)
end
--select count(*) from tb_group
--方法一
-- TOP N 分頁
create proc sp_pagination1
@pageindex int=1,
@pagesize int=10,
@ordercolumn nvarchar(20)='''',
@order bit =0,-- 0 is''ASC'' 1 is ''DESC''
@totalcount int output
as
declare @sql nvarchar(4000)
declare @strOrder1 nvarchar (4)
declare @strOrder2 nvarchar (4)
declare @strOrderColumn nvarchar(60)
select @totalcount=count(id) from tb_group
if (@order=0)
begin
set @strOrder1 =''asc''
set @strOrder2 =''desc''
end else begin
set @strOrder1 =''desc''
set @strOrder2 =''asc''
end
if (@ordercolumn<>'''')
begin
set @strOrderColumn=''order by '' + @ordercolumn + '' ''
end else begin
set @strOrderColumn=''''
set @strOrder1 =''''
set @strOrder2 =''''
end
if (@pageindex<=0 or @pagesize<=0 or (@pagesize*(@pageindex-1)>@totalcount)) begin
set @sql=''select * from tb_group where 1<>1''
end else begin
if (@pagesize*@pageindex>@totalcount)
begin
set @sql=''select * from ''+
''(select top '' + convert(nvarchar(10),@totalcount-(@pagesize*(@pageindex-1))) +
'' * from tb_group '' + @strOrderColumn + @strOrder2 +'',id desc) t1 '' +
@strOrderColumn + @strOrder1 +'',id''
end else begin
set @sql=''select * from ''+
''(select top '' +convert(nvarchar(10),@pagesize)+'' * from '' +
''(select top '' + convert(nvarchar(10),@pagesize+@pagesize*(@pageindex-1)) +
'' * from tb_group '' + @strOrderColumn + @strOrder1+ '',id) t1 '' + @strOrderColumn + @strOrder2 +
'',id desc) t2 '' + @strOrderColumn + @strOrder1 +'',id''
end
end
exec (@sql)
--方法二
-- 臨時表分頁
create proc sp_pagination2
@pageindex int=1,
@pagesize int=10,
@ordercolumn nvarchar(20)='''',
@order bit =0,-- 0 is''ASC'' 1 is ''DESC''
@totalcount int output
as
declare @sql nvarchar(4000)
declare @strOrder nvarchar (4)
declare @strOrderColumn nvarchar(60)
if (@order=0)
begin
set @strOrder =''asc''
end else begin
set @strOrder =''desc''
end
if (@ordercolumn<>'''')
begin
set @strOrderColumn=''order by '' + @ordercolumn + '' ''
end else begin
set @strOrderColumn=''''
set @strOrder =''''
end
select @totalcount=count(id) from tb_group
create table #pager (id int, pagerid int identity(1, 1) not null)
set @sql= ''insert into #pager (id) '' +
''select top '' +convert(varchar(10),@pageindex*@pagesize) + '' id from tb_group '' + @strOrderColumn + @strOrder + '',id''
exec (@sql)
set @sql=''select * from tb_group as A inner join #pager as B on A.id=B.id'' +
'' where b.pagerid > ''+ convert(varchar(10),@pagesize * (@pageindex-1))+'' and b.pagerid < '' +
convert(varchar(10),@pagesize * (@pageindex) +1)
exec (@sql)
--方法三
-- ROW_Number分頁
--在開始測試這個方法之前我發現一個問題,下面兩個語句照道理應該一模一樣,可是上面用小寫英文的卻無法執行,查詢KB也沒有發現相關問題。莫非我發現了一個BUG?
select * from (select *,row_number() over (order by country) as rowno from tb_group) as t1
where rowno > 0 and rowno < 61
SELECT * FROM (SELECT *,ROW_NUMBER() OVER (ORDER BY country) AS RowNo FROM tb_group) AS T1
WHERE RowNo > 30 and RowNo < 61
--下面是測試代碼
create proc sp_pagination3
@pageindex int=1,
@pagesize int=10,
@ordercolumn nvarchar(20)='''',
@order bit =0,-- 0 is''ASC'' 1 is ''DESC''
@totalcount int output
as
declare @sql nvarchar(4000)
declare @strOrder nvarchar (4)
declare @strOrderColumn nvarchar(60)
if (@order=0)
begin
set @strOrder =''asc''
end else begin
set @strOrder =''desc''
end
if (@ordercolumn<>'''')
begin
set @strOrderColumn=''order by '' + @ordercolumn + '' ''
end else begin
set @strOrderColumn=''''
set @strOrder =''''
end
select @totalcount=count(id) from tb_group
set @sql= ''SELECT * FROM (SELECT *,ROW_NUMBER() OVER ('' + @strOrderColumn + @strOrder +'') AS RowNo FROM tb_group) AS T1'' +
'' WHERE RowNo > ''+ convert(varchar(10),@pagesize * (@pageindex-1)) +
'' and RowNo < '' + convert(varchar(10),@pagesize * (@pageindex) +1)
exec (@sql)
測試用例代碼:
--測試在排序 “無索引列” 時的分頁性能
DBCC FREEPROCCACHE
GO
select * from tb_group order by country asc
--DBCC FREESESSIONCACHE
declare @starttime datetime
declare @endtime datetime
declare @time datetime
declare @total int
set @starttime=getdate()
exec sp_pagination3 1,10,''country'',0,@total output
set @endtime=getdate()
set @time=@endtime-@starttime
select @total,@time
--測試在排序 “有索引列” 時的分頁性能
DBCC FREEPROCCACHE
GO
--DBCC FREESESSIONCACHE
select * from tb_group
declare @starttime datetime
declare @endtime datetime
declare @time datetime
declare @total int
set @starttime=getdate()
exec sp_pagination3 3,5,''id'',0,@total output
set @endtime=getdate()
set @time=@endtime-@starttime
select @total,@time
利用TOP N 分頁
測試項目
測試用時
無索引列排序
有索引列排序
每頁顯示30條記錄,返回第10頁 :
1:05分220毫秒
每頁顯示30條記錄,返回第1000頁 :
1:07分
280毫秒
每頁顯示30條記錄,返回第10000頁 :
1:07分
640毫秒
每頁顯示20條記錄,返回第100000頁 :
1:15分
2秒640毫秒
臨時表分頁
測試項目
測試用時
無索引列排序
有索引列排序
每頁顯示30條記錄,返回第10頁 :
27秒407毫秒
2秒627毫秒
每頁顯示30條記錄,返回第1000頁 :
28秒170
2秒873毫秒
每頁顯示30條記錄,返回第10000頁 :
31秒500毫秒
5秒783毫秒
每頁顯示20條記錄,返回第100000頁 :
45秒937毫秒
15秒750毫秒
ROW_NUMBER 分頁
測試項目
測試用時
無索引列排序
有索引列排序
每頁顯示30條記錄,返回第10頁 :
9秒107毫秒
250毫秒
每頁顯示30條記錄,返回第1000頁 :
9秒873毫秒
263毫秒
每頁顯示30條記錄,返回第10000頁 :
12秒230毫秒
517毫秒
每頁顯示20條記錄,返回第100000頁 :
19秒640毫秒
1秒983毫秒
從上面的分析可以看出,在有條件的情況下(有SQL2005)應該優先考慮使用ROW_NUMBER 排序。此外,如果僅對於索引列排序,可以使用TOP N排序獲得較好的性能。但是如果需要使用無索引列排序,可以考慮使用臨時表。或者修改表結構建立索引。