@id varchar(255), --需要排序的不重复的id号
@sort varchar(255) --排序字段及规则
)
as
declare @str nvarchar(4000)
set @str='select top '+cast(@recsperpage as varchar(20))+' * from ('+@sql+') t where t.'+@id+'not in
(select top '+cast((@recsperpage*(@page-1)) as varchar(20))+' '+@id+' from ('+@sql+') t9 order by '+@sort+') order by '+@sort
print @str
exec sp_executesql @str
go
其实,以上语句可以简化为:
select top 页大小 *
from table1
where (id not in
(select top 页大小*页数 id
from 表
order by id))
order by id
但这个存储过程有一个致命的缺点,就是它含有not in字样。虽然我可以把它改造为:
select top 页大小 *
from table1
where not exists
(select * from (select top (页大小*页数) * from table1 order by id) b where b.id=a.id )
order by id
即,用not exists来代替not in,但我们前面已经谈过了,二者的执行效率实际上是没有区别的。
既便如此,用top 结合not in的这个方法还是比用游标要来得快一些。
虽然用not exists并不能挽救上个存储过程的效率,但使用sql server中的top关键字却是一个非常明智的选择。因为分页优化的最终目的就是避免产生过大的记录集,而我们在前面也已经提到了top的优势,通过top 即可实现对数据量的控制。
在分页算法中,影响我们查询速度的关键因素有两点:top和not in。top可以提高我们的查询速度,而not in会减慢我们的查询速度,所以要提高我们整个分页算法的速度,就要彻底改造not in,同其他方法来替代它。
我们知道,几乎任何字段,我们都可以通过max(字段)或min(字段)来提取某个字段中的最大或最小值,所以如果这个字段不重复,那么就可以利用这些不重复的字段的max或min作为分水岭,使其成为分页算法中分开每页的参照物。在这里,我们可以用操作符“>”或“<”号来完成这个使命,使查询语句符合sarg形式。如:
select top 10 * from table1 where id>200
于是就有了如下分页方案:
select top 页大小 *
from table1
where id>
(select max (id) from
(select top ((页码-1)*页大小) id from table1 order by id) as t
)
order by id
在选择即不重复值,又容易分辨大小的列时,我们通常会选择主键。下表列出了笔者用有着1000万数据的办公自动化系统中的表,在以gid(gid是主键,但并不是聚集索引。)为排序列、提取gid,fariqi,title字段,分别以第1、10、100、500、1000、1万、10万、25万、50万页为例,测试以上三种分页方案的执行速度:(单位:毫秒)
页 码
方案1
方案2
方案3
1
60
30
76
10
46
16
63
100
1076
720
130
500
540
12943
83
1000
17110
470
250
1万
24796
4500
140
10万
38326
42283
1553
25万
28140
128720
2330
50万
121686
127846
7168