首页 > 开发 > 综合 > 正文

数据库分页方法

2024-07-21 02:12:12
字体:
来源:转载
供稿:网友

最大的网站源码资源下载站,

方法一:

select top 10 b.* from (select top 20 主键字段,排序字段 from 表名 order by 排序字段 desc) a,表名 b where b.主键字段 = a.主键字段 order by a.排序字段

10 = 每页记录数

20 = (当前页 + 1) * 每页记录数

以上语句即可以实现分页,但是最后取出的结果排序是升序,如果需要结果集为降序(例如时间),则有两种方法可以处理

1.使用以下语句,但效率可能要降低一些

select * from 表名 b, (select top 10 主键字段,排序字段 from (select top 20 主键字段,排序字段 from 表名 order by 排序字段 desc) a order by 排序字段 ) c where b.主键字段 = c.主键字段 order by c.排序字段 desc

2.在ado里处理,将记录集游标移到最后,然后前移

''以下为asp范例

set rstemp = server.createobject("adodb.recordset")

rstemp.open 语句,conn,1,1

rstemp.movelast

for i = 1 to rstemp.recordcount

 '取值....

rstemp.moveprevious

next

 

经测试,以上分页方法比使用临时表分页速度还要快,并且简单易用.

方法二:

大数据量下的分页

    对于非常大的数据模型而言,分页检索时,每次都加载整个数据源非常浪费。通常的选择是检索页面大小的块区的数据,而非检索所有的数据,然后单步执行当前行。

    本文演示asp.net的datagrid和sql server 实现大数据量下的分页,为了便于实现演示,数据表采用了northwind数据库的orders表(830条记录)。

    如果数据表中有唯一的自增索引,并且这个字段没有出现断号现象。检索页面大小的块区数据就非常简单了。通过简单的sql语句就可以实现这个功能:

    select * from orders where orderid between 10248 and 10253

    其中,开始编号为:(currentpageindex - 1) * pagesize  结束编号为:currentpageindex * pagesize

    当然,如果这个字段断号不是很严重,而且允许不是很严格的按照每页条数分页,这样的方法也是可以用的。

    如果这个字段断号,或者需要按照其他条件排序分页,就要复杂些了。首先要获得这个页面需要显示的编号,然后再按照这个编号获得需要的块区数据。根据编号获得块区数据很简单。不过用下面方式获得数据排序并不是按照指定的id列表顺序,这时候还要附加order by 命令。

select * from orders where orderid in (10248,10249,10250,10251,10252,10253) order by orderid desc

    获得这个页面需要显示的编号列表就复杂多了,而且有多种方案:

方案一:维护一个表,这个表记录需要显示的这些编号排序顺序。(这个表可以是临时表,也可以是物理表)。下面演示了利用一个全局临时表。这个全局临时表记录需要显示的编号。注意排序,这里的order by 就是需要显示的排序顺序。

create table ##temptable(iid int identity (1, 1) not null,mainid int not null)insert ##temptable(mainid) select orderid from orders order by orderid descselect * from ##temptabledrop table ##temptable -- 实际执行时候,删除全部临时表当然不再这里执行。

这个临时表存在,获得指定分页的分块数据就很简单了。看下面代码:

create table ##temptable(iid int identity (1, 1) not null,mainid int not null)insert ##temptable(mainid) select orderid from orders order by orderid descdeclare @pagesize int,@currpage int,@strsql varchar(2000),@idstr varchar(1000)select @pagesize = 30select @currpage = 2select @idstr = ''select @idstr = @idstr + ltrim(rtrim(str(mainid))) + ',' from ##temptable where iid between ((@currpage-1)*@pagesize+1) and @currpage*@pagesizeif @idstr <> '' begin select @idstr = left(@idstr,len(@idstr)-1)endselect @strsql = 'select * from orders where orderid in ('[email protected]+')  order by orderid desc 'exec(@strsql)drop table ##temptable

注意:实际使用这个方案的时候,还要考虑何时更新这个全局临时表,一般是放到计划任务中,定时更新这个汇总表。

方案二:每次都去查询,每次获得最新的编号顺序。由于这时候不存在这个临时表,书写获得需要显示页面的编号的字符串就需要点技巧,看下面的代码:asp.net 的 datagrid 提供了使用这种分区的数据的方法。 datagrid 通过 allowcustompaging 和 virtualitemcount 属性支持块区操作。如果 allowcustompaging 为 true,则 datagrid 不会根据 currentpageindex 计算数据模型中的起始显示位置。datagrid 将显示数据模型中的所有数据,而页导航栏将当前位置报告为 (virtualitemcount+pagesize-1)/pagesize 之 currentpageindex 页。下面的示例说明此功能。

declare @pagesize int,@currpage int,@topnum int,@previous intselect @pagesize = 30select @currpage = 2select @topnum = @currpage * @pagesizeselect @previous = (@currpage - 1) * @pagesizedeclare @i int,@idstr nvarchar(500),@strsql nvarchar(1000)select @i = 0select @strsql = n''select @strsql = @strsql + n' select top '+str(@topnum)+ ' @i = @i + 1 'select @strsql = @strsql + n',  @idstr = 'select @strsql = @strsql + n'case when @i > '+str(@previous)+' then  @idstr + ltrim(rtrim(str(orderid))) + '','' 'select @strsql = @strsql + n'else n''''end 'select @strsql = @strsql + n'from orders 'select @strsql = ltrim(rtrim(@strsql)) + n' order by orderid desc 'select @idstr = n''exec sp_executesql @strsql,n'@i int,@idstr varchar(500) output',@i,@idstr outputif len(rtrim(ltrim(@idstr))) > 0begin select @idstr = left(@idstr,len(@idstr)-1)endselect @strsql = 'select * from orders where orderid in ('[email protected]+')'exec(@strsql)

 

     protected void binddatagrid(int currpage) {  string strconn = "data source=(local);integrated security=sspi;database=northwind";  // 请确认 机器名/aspnet 用户可以访问northwind数据库  sqlcommand cmd = new sqlcommand();  sqlconnection conn = new sqlconnection(strconn);  sqlparameter[]  parms = new sqlparameter[] {   new sqlparameter("@pagesize",sqldbtype.int),   new sqlparameter("@currpage",sqldbtype.int),   new sqlparameter("@searchsql",sqldbtype.nvarchar,128),   new sqlparameter("@count",sqldbtype.int),  };  parms[0].value = datagrid1.pagesize;  parms[1].value = (currpage+1);   //  数据库的分页算法第一页是1  datagrid的第一页是0  parms[2].value = dbnull.value;  parms[3].direction = parameterdirection.output;  parms[3].value = dbnull.value;  dataset ds = new dataset();  try   {   if (conn.state != connectionstate.open) conn.open();   cmd.connection = conn;   cmd.commandtext = "selected_page_list";   cmd.commandtype = commandtype.storedprocedure;   if (parms != null)    {    foreach (sqlparameter parm in parms)     cmd.parameters.add(parm);   }   sqldataadapter da = new sqldataadapter(cmd);   da.fill(ds);   int aa = convert.toint32(parms[3].value.tostring());   cmd.parameters.clear();   if (currpage == 0)   {    datagrid1.virtualitemcount = aa;   }   datagrid1.currentpageindex = currpage;   datagrid1.datasource = ds;   datagrid1.databind();  }  catch(exception ewx)  {   conn.close();   response.write (ewx.message.tostring());   response.end();  } }    void page_load(object sender, eventargs e ) {  if (!ispostback)   {   binddatagrid(0);   // 第一次打开这个页面,访问分页的第一页  }    }    void mydatagrid_page(object sender, datagridpagechangedeventargs e) {  binddatagrid(e.newpageindex);    }

如果你有更多数据量的表稍加修改,也可以使用本演示程序。其下是演示代码下载,演示代码使用的是方案二。使用方法看readme.txt文件。

整个演示代码 下载

http://chs.gotdotnet.com/quickstart/aspplus/samples/webforms/ctrlref/webctrl/datagrid/doc_datagrid.aspx#paging
这里演示了利用datagrid 的这个功能(没有本文中讨论的利用存储过程获得分区数据)。如对datagrid的这个功能不太熟悉,请先看这里。

 

方法三:

虽然 datagrid 控件自己带了一个分页处理机制,但它是将符合查询条件的所有记录读入内存,然后进行分页显示的。随着符合条件的记录数目增多,就会出现运行效率问题,或者至少是资源的利用率下降。

下面的代码示例都以下面的表结构为准:

 

 articles 表sql server 类型oracle 类型
pkidint (自增)number(9) (插入时在当前最大值上加1)
 authornvarchar(10)nvarchar2(10)
 titlenvarchar(50)nvarchar2(50)
 pubtimedatetimedate

sql server / access 等微软产品中,我们通常的自定义分页有两种思路:

一种是以 asp.net forum 为代表的、“临时表”方法:即在存储过程中建立一个临时表,该临时表包含一个序号字段(1,2,3,....)以及表的主键(其他能够唯一确定一行记录的字段也是可以的)字段。存储过程可能如下:(编号 ss1)

create procedure getallarticles_paged
(
     @pageindex int,
     @pagesize int,
     @totalrecords out int,
     @totalpages out int
)
as

declare @pagelowerbound int
declare @pageupperbound int

-- set the page bounds
set @pagelowerbound = @pagesize * @pageindex
set @pageupperbound = @pagelowerbound + @pagesize + 1

-- create a temp table to store the select results
create table #tmp
(
     recno int identity (1, 1) not null,
     articleid int
)

insert into #tmp
     select [id]
     from articles
     order by pubtime desc

select a.*
from articles a (nolock), #tmp t
where a.id = t.articleid and
     t.recno > @pagelowerbound and
     t.recno < @pageupperbound
order by t.recno

go

另一种可能更适合程序中“拼凑” sql 语句:用两次 top 命令取得我们所要的分页数据,例如:(编号 ss2)

select * from
     (
     select top(pagesize) * from
     (
          select top (pagesize * pageindex) *
          from articles
          order by pubtime desc
     )
     order by pubtime asc
)
order by pubtime desc

这个的想法就是“掐头去尾”,还有不少分页的方法,这里就不一一列出了。

对于 oracle 数据库,有几处不同严重妨碍了上面几个方法的实施,比如,oracle 不支持 top 关键字:不过这个好像并不十分严重,因为它提供了 rownum 这个隐式游标,可以实现与 top 类似的功能,如:

select top 10 ... from where ...

要写成

select ... from ... where ... and rownum <= 10

rownum 是记录序号(1,2,3...),但有一个比较麻烦的事情是:如果 sql 语句中有 order by ... 排序的时候,rownum 居然是先“标号”后排序!这样,这个序号如果不加处理是不合乎使用需求的。

至于临时表,oracle 的临时表和 sql server 的有很大不同,我还没搞懂这个东西,就不妄加揣测了。

国内网站中介绍 oracle 分页的资料很少,我找到了一个国外站点(www.faqts.com)的一篇 faq,根据这篇文章的介绍,可以如下分页:(编号 ora1)

select * from
     (
     select a.*, rownum r
     from
          (
          select *
          from articles
          order by pubtime desc

          ) a
     where rownum <= pageupperbound
     ) b
where r > pagelowerbound;

其中蓝色部分可以改为任意的、需要的 sql select 语句,这点倒是挺方便的。

方法四:

今天突然发现,oracle原来可以这样实现分页功能:

select * from (select rownum rdd,field1,field2 from t_table where rownum<=400) where  rdd>200

上述语句实现了从第201条记录开始处取200条记录

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表