目前比较广泛使用的分页方式是将查询结果缓存在HttpSession或有状态bean中,翻页的时候从缓存中取出一页数据显示。这种方法有两个主要的缺点:一是用户可能看到的是过期数据;二是如果数据量非常大时第一次查询遍历结果集会耗费很长时间,并且缓存的数据也会占用大量内存,效率明显下降。
其它常见的方法还有每次翻页都查询一次数据库,从ResultSet中只取出一页数据(使用rs.last();rs.getRow()获得总计录条数,使用rs.absolute()定位到本页起始记录)。这种方式在某些数据库(如oracle)的JDBC实现中差不多也是需要遍历所有记录,实验证明在记录数很大时速度非常慢。
至于缓存结果集ResultSet的方法则完全是一种错误的做法。因为ResultSet在Statement或Connection关闭时也会被关闭,如果要使ResultSet有效势必长时间占用数据库连接。
因此比较好的分页做法应该是每次翻页的时候只从数据库里检索页面大小的块区的数据。这样虽然每次翻页都需要查询数据库,但查询出的记录数很少,网络传输数据量不大,如果使用连接池更可以略过最耗时的建立数据库连接过程。而在数据库端有各种成熟的优化技术用于提高查询速度,比在应用服务器层做缓存有效多了。
在oracle数据库中查询结果的行号使用伪列ROWNUM表示(从1开始),rownum是在查询之后排序之前赋值的.
select * from (
select a.*, rownum r__
from (
select *
from employee
where name like 'A%'
order by birthday DESC
) a
where rownum < ((pageNum*pageSize)+1)
)
where r__ >= ((pageNum-1)*pageSize +1)
mySQL可以使用LIMIT子句:
select *
from employee
where name like 'A%'
order by birthday DESC
LIMIT ((pageNumber -1) * pageSize), pageSize
DB2有rownumber()函数用于获取当前行数:
SELECT * FROM
(
SELECT a.* , rownumber() over () AS rn
FROM
(
SELECT * FROM EMPLOYEE WHERE NAME LIKE 'A%'
ORDER BY BIRTHDAY DESC
) AS a
) AS rs
WHERE rs.rn between (((pageNumber-1) * pageSize)+1) AND (pageNumber * pageSize)
sql server中用row_number() 函数
WITH Members AS
(
SELECT NAME,BIRTHDAY
ROW_NUMBER() OVER (ORDER BY BIRTHDAY DESC) AS RowNumber
FROM EMPLOYEE
)
SELECT *
FROM Members
WHERE RowNumber BETWEEN (((pageNumber-1) * pageSize)+1) AND (pageNumber * pageSize)
ORDER BY RowNumber ASC;