数据库分页-2

作者:chilleen 来源:ITPOW(原创) 日期:2006-6-2

上一节所说的分页方法,最大的缺点就是占用内存,因为它是把所有的记录都取出来,再进行分页,假如有一万条记录(新闻类),那么这个内存占用量是非常可怕的,甚至可能使整个网站停止服务,但因为它程序简单,提取记录时可以灵活应用索引,使用在记录较少(比如几百条)时很适用。

根据这个分页的缺点,人们又制作了第二个分页方法,没有人命名,我们暂且命名为 B1,并不是想要冠名,只是方便下面的讨论。

if page <= 1 then
    sql = "select top " & pageSize & " * from tbl where...order by..."
else
    sql = "select top " & pageSize & " * from tbl" & _
          " where id not in (select top " & pageSize*(page-1) & " id from tbl where...order by...)" & _
          " ...order by..."
end if
rs.Open sql, conn, 1, 1
do while not rs.eof
    ...
    rs.MoveNext
loop
rs.Close

上述中,pageSize 为分页大小,page 为当前要显示的页。当 page <= 1(显示第一页)时,sql 语句很简单,也很高效。当 page > 1 时,SQL 查询具有一个子查询,子查询使得不必将所有的记录都取出来,假设 page = 5,那么可以这样解释,取位于前 5 页且不位于第 4 页的记录,也就是取第 5 页的记录,由于只取需要的记录,我们可以看 do while 循环时没有了 rowCnt。

有人说其中有 in,会引起全表扫描,效率不高,不如改下下面的语句,我们暂且命名为 B2。

sql = "select top " & pageSize & " * from tbl" & _
      " where id<(select top 1 id from (select top " & pageSize*(page-1) & " id from tbl order by id desc) as a order by id) order by id desc"

B2 包含两个子查询,第一个子查询跟 B1 的第一个子查询功能相同,第二个子查询是取出前 page-1 页中的最后一个 id,然后再根据找出 id 小于最后一个 id 的前 pageSize 个记录。

一般情况下(实际情况要看表的数据量,表的结构,所查询的页码,建议根据实际情况进行测试对比),执行计划里 B2 的效率要低些,但那毕竟只是计划,实际中 B2 的执行效率的确比 B1 方法的执行效率高,但这并不是因为 B1 使用了 in 造成了全表扫描而造成的,要知道,有 in 并不代表就会引起全表扫描,当页数 > 1 时, B1 有两个聚集索引扫描,并对两个扫描的结果进行匹配,并不涉及全表扫描。

但方法 B2 有一个缺点,就是 id 必须是唯一的,且排序方法只按 id 排列,一般内容管理系统是按文章的时间来排列,而文章的时间不一定是唯一的,所以应用范围有限。

相关文章