请教一个数据库问题:如何分页显示记录

时间:2021-07-10 10:18:22
有这样一个表格,里面有大约100万条记录。用一个查询语句查出所有的记录(当然不可能全部返回了),想做成分页显示,比如每页有100条,那么第一页只显示序号为1到100的记录。依次类推。
本来只显示100条记录应该是很快的,但是实际运行速度却很慢,我估计是把所有的记录全给返回了,然后再挑出100条来显示。不知道有什么方法可以获取从给定的位置开始给定数目的记录,并且系统开销很小。

不要告诉我用主键ID, 因为排序不一定是按照主键来的。实际上除了主键也没有其他的索引(因为重复率很高)。

16 个解决方案

#1


>>>>但是实际运行速度却很慢

把你的SQL语句贴出来, 或这么做

如果你排序的话,第一次用
select top 100 * from yourtable order by SomeField

记住每页的第一个和最后一个记录

往后翻页,你用
select top 100 * from yourtable where SomeField > '最后一个记录' order by 
SomeField

往前翻页,你用
select * from (select top 100 * from yourtable where SomeField < '第一个
记录' order by SomeField desc ) t order by SomeField


>>>>不要告诉我用主键ID, 因为排序不一定是按照主键来的。实际上除了主键也没有其他的>>>>索引

速度慢冤不得人, 祝你好运!

#2


一般的方法
select Top pageSize *
  from T
 where SortField NOT IN (select Top pageSize*pagei SortField
                          from T
                         order by SortField )
order by SortField

例如,每页7条,列印第7页:
select Top 10 *
  from T
 where SortField NOT IN (select Top 70 SortField
                          from T
                         order by SortField )
order by SortField


or 

CREATE PROCEDURE Get_Customers_By_Page
@CurrentPage int,
@PageSize int,
@TotalRecords int output
as

SELECT identity(int,1,1) as id ,* into  #TempTable from table

DECLARE @FirstRec int, @LastRec int
SELECT @FirstRec = (@CurrentPage - 1) * @PageSize
SELECT @LastRec = (@CurrentPage * @PageSize + 1)

SELECT 
  *
FROM 
  #TempTable
WHERE 
  ID > @FirstRec 
AND
  ID < @LastRec

SELECT @TotalRecords = COUNT(*) FROM table

#3


create procedure cur_num @num int
as
declare @sql  varchar(8000)
set @sql = 'select top '+ cast(@num as varchar(100)) + ' * from 表'
exec(@sql)

#4


修改
create procedure cur_num @num int,@num1
as
declare @sql  varchar(8000)
set @sql = 'select top '+ cast(@num as varchar(100)) + ' * from 表 where id >@num1'
exec(@sql)

#5


to: happydreamer(黑DD) 
你说的意思我明白,我也这样想过,但它的效率直在值得商榷,
比如说我想取得第 500001到500100 的100条记录,那么你的语句就是:
select Top 10 *  from T
 where SortField NOT IN (select Top 500000 SortField
 from T  order by SortField )order by SortField
效率很低吧。你的另一种方法也一样,先把这么多记录倒到另一个临时表中,也一定很慢。


to: saucer(思归, MS .NET MVP) :
你说的第一页用 select top 100 * ... 我能理解。但是以后的记录你用下面的语句:
select top 100 * from yourtable where SomeField > '最后一个记录' order by 
SomeField
就有一个假设:SomeField 必须是唯一索引。 如果根本就没有索引的话,该怎么办呢。
我想要的其实就是类似 select between 500001 and 500100 * from T 的这样一个语句。但又要顾及到效率。


to wgy2008(北极光) :
你说的也只是显示头一百条时的情况。

#6


SQLServer中分页只能这样了:

exec('SELECT Top '+cast(@每页大小 as varchar)+' * FROM T WHERE SortField NOT IN (SELECT TOP '+cast(@每页大小*@第几页 as varchar)+' SortField from T )')

只有oracle里有rowid速度也很快,期待sqlserver下一个版本有rowid

#7


不外乎,利用触发器,在记录变更时重新计算变更记录以后的行号。

#8


唉,看来是没有很好的解决方法了。
以上的解决方法无非有以下几种: 
1. 用 select top 100 * from T where .. not in ( select top beginIndex ... ), 但是当 beginIndex 很大的时候,比如 1000000, 这样做无疑效率很低。

2. 利用索引,把每次查询后的索引值记录。但是当索引值重复很大或者根本没有索引时,这种方法就不行了。

3. 用类似 oracle里的rowid, 但是 sql-server 里没有。 :-(

我现在用的是 ado.net 里的 DbDataAdapter.Fill 方法里的功能来实现分页的,但是效率很低,所以想找一种能直接在数据库里实现的方法。

各位大侠帮忙啊

#9


那些都是“直接在数据库了实现”的方法啊!!!!

只有“ado.net 里的 DbDataAdapter.Fill ”才是在客户端实现的方法。

#10


傻了吧叽的ado.net是怎么做的呢?他把100万数据读到客户端,然后只留下需要的一页数据,剩下的再丢弃。

所以,即使你认为“很慢”的查询语句,也比你用ado.net的所谓分页功能要快不知道多少倍。

#11


这里,之所以慢,不在于服务器上查询中间结果需要的数据的多,而在于要将过多的结果数据在网络上传送到客户端。

#12


楼上说的对!

exec('SELECT Top '+cast(@每页大小 as varchar)+' * FROM T WHERE SortField NOT IN (SELECT TOP '+cast(@每页大小*@第几页 as varchar)+' SortField from T )')

肯定比你ado.net里做的快!

#13


SQLServer中分页这样比较好:

exec('SELECT Top '+cast(@每页大小 as varchar)+' * FROM T WHERE SortField NOT IN (SELECT TOP '+cast(@每页大小*@第几页 as varchar)+' SortField from T )')

速度不是很快,但是没有什么问题

#14


谢谢w_rose,你说得对。我回去试一试,星期一再结帐

#15


谢谢 happydreamer(黑DD) 及各位的回答,我试了一下,你们是对的。

本来最好是数据库直接支持,即然可以有 select top N ..., 为什么不可以有类似
select between N1 and N2 ...的语句呢?
数据库直接支持的效率显示是最高的

#16


学习中

#1


>>>>但是实际运行速度却很慢

把你的SQL语句贴出来, 或这么做

如果你排序的话,第一次用
select top 100 * from yourtable order by SomeField

记住每页的第一个和最后一个记录

往后翻页,你用
select top 100 * from yourtable where SomeField > '最后一个记录' order by 
SomeField

往前翻页,你用
select * from (select top 100 * from yourtable where SomeField < '第一个
记录' order by SomeField desc ) t order by SomeField


>>>>不要告诉我用主键ID, 因为排序不一定是按照主键来的。实际上除了主键也没有其他的>>>>索引

速度慢冤不得人, 祝你好运!

#2


一般的方法
select Top pageSize *
  from T
 where SortField NOT IN (select Top pageSize*pagei SortField
                          from T
                         order by SortField )
order by SortField

例如,每页7条,列印第7页:
select Top 10 *
  from T
 where SortField NOT IN (select Top 70 SortField
                          from T
                         order by SortField )
order by SortField


or 

CREATE PROCEDURE Get_Customers_By_Page
@CurrentPage int,
@PageSize int,
@TotalRecords int output
as

SELECT identity(int,1,1) as id ,* into  #TempTable from table

DECLARE @FirstRec int, @LastRec int
SELECT @FirstRec = (@CurrentPage - 1) * @PageSize
SELECT @LastRec = (@CurrentPage * @PageSize + 1)

SELECT 
  *
FROM 
  #TempTable
WHERE 
  ID > @FirstRec 
AND
  ID < @LastRec

SELECT @TotalRecords = COUNT(*) FROM table

#3


create procedure cur_num @num int
as
declare @sql  varchar(8000)
set @sql = 'select top '+ cast(@num as varchar(100)) + ' * from 表'
exec(@sql)

#4


修改
create procedure cur_num @num int,@num1
as
declare @sql  varchar(8000)
set @sql = 'select top '+ cast(@num as varchar(100)) + ' * from 表 where id >@num1'
exec(@sql)

#5


to: happydreamer(黑DD) 
你说的意思我明白,我也这样想过,但它的效率直在值得商榷,
比如说我想取得第 500001到500100 的100条记录,那么你的语句就是:
select Top 10 *  from T
 where SortField NOT IN (select Top 500000 SortField
 from T  order by SortField )order by SortField
效率很低吧。你的另一种方法也一样,先把这么多记录倒到另一个临时表中,也一定很慢。


to: saucer(思归, MS .NET MVP) :
你说的第一页用 select top 100 * ... 我能理解。但是以后的记录你用下面的语句:
select top 100 * from yourtable where SomeField > '最后一个记录' order by 
SomeField
就有一个假设:SomeField 必须是唯一索引。 如果根本就没有索引的话,该怎么办呢。
我想要的其实就是类似 select between 500001 and 500100 * from T 的这样一个语句。但又要顾及到效率。


to wgy2008(北极光) :
你说的也只是显示头一百条时的情况。

#6


SQLServer中分页只能这样了:

exec('SELECT Top '+cast(@每页大小 as varchar)+' * FROM T WHERE SortField NOT IN (SELECT TOP '+cast(@每页大小*@第几页 as varchar)+' SortField from T )')

只有oracle里有rowid速度也很快,期待sqlserver下一个版本有rowid

#7


不外乎,利用触发器,在记录变更时重新计算变更记录以后的行号。

#8


唉,看来是没有很好的解决方法了。
以上的解决方法无非有以下几种: 
1. 用 select top 100 * from T where .. not in ( select top beginIndex ... ), 但是当 beginIndex 很大的时候,比如 1000000, 这样做无疑效率很低。

2. 利用索引,把每次查询后的索引值记录。但是当索引值重复很大或者根本没有索引时,这种方法就不行了。

3. 用类似 oracle里的rowid, 但是 sql-server 里没有。 :-(

我现在用的是 ado.net 里的 DbDataAdapter.Fill 方法里的功能来实现分页的,但是效率很低,所以想找一种能直接在数据库里实现的方法。

各位大侠帮忙啊

#9


那些都是“直接在数据库了实现”的方法啊!!!!

只有“ado.net 里的 DbDataAdapter.Fill ”才是在客户端实现的方法。

#10


傻了吧叽的ado.net是怎么做的呢?他把100万数据读到客户端,然后只留下需要的一页数据,剩下的再丢弃。

所以,即使你认为“很慢”的查询语句,也比你用ado.net的所谓分页功能要快不知道多少倍。

#11


这里,之所以慢,不在于服务器上查询中间结果需要的数据的多,而在于要将过多的结果数据在网络上传送到客户端。

#12


楼上说的对!

exec('SELECT Top '+cast(@每页大小 as varchar)+' * FROM T WHERE SortField NOT IN (SELECT TOP '+cast(@每页大小*@第几页 as varchar)+' SortField from T )')

肯定比你ado.net里做的快!

#13


SQLServer中分页这样比较好:

exec('SELECT Top '+cast(@每页大小 as varchar)+' * FROM T WHERE SortField NOT IN (SELECT TOP '+cast(@每页大小*@第几页 as varchar)+' SortField from T )')

速度不是很快,但是没有什么问题

#14


谢谢w_rose,你说得对。我回去试一试,星期一再结帐

#15


谢谢 happydreamer(黑DD) 及各位的回答,我试了一下,你们是对的。

本来最好是数据库直接支持,即然可以有 select top N ..., 为什么不可以有类似
select between N1 and N2 ...的语句呢?
数据库直接支持的效率显示是最高的

#16


学习中