随心所欲

做个幸福的人
posts - 146, comments - 1380, trackbacks - 25, articles - 0
  博客园 :: 首页 :: 新随笔 ::  :: 订阅 订阅 :: 管理

浅谈分页技术

Posted on 2007-01-10 15:06 随心所欲 阅读(5443) 评论(63)  编辑 收藏 所属分类: ORM/DB

这几天看到几篇关于分页的文章,觉得有些东西可以分享一下。这里提供一些关于UI分页和数据库分页的一些解决方案。还有一些常用控件的使用方法概要

通常一个查询语句如果得到的数据量过大或者为了UI美观考虑,就需要进行对这些数据进行分页显示。

分页其实有两类,界面上分页和数据库分页。

 

关于数据显示控件

Repeater

  这个是最基本的数据集帮订显示控件。不支持分页

DataList

  比上一个功能要复杂一点,但是也不支持分页

GridView(DataGrid)

  支持分页,功能最强大。效率也是最低的一个。

当然,也可以写自己的分页控件,或者包装一个现有的。

 

数据库分页

  事实上,数据库分页依赖于具体数据库。每种数据库提供的语句不太一样,具体操作要根据具体的数据库才决定。

  比方说sql server sql server数据分页 比较 http://www.cnblogs.com/dlwang2002/archive/2005/10/14/254971.html

  比方说oracle:它提供了一个RowNum列,所以分页算法很容易实现。Sql server 2005里面也提供了类似的函数。
          string format = "SELECT {0} FROM (SELECT RowNum AS RowIndex, {0} FROM ("

                       + "SELECT {0} FROM {1}{2}{3}) WHERE RowNum <= {4}) WHERE RowIndex > {5}";

 其他的, 比方说

mysql"SELECT * LIMIT {0} OFFSET {1}"

 PostgreSql: "SELECT * LIMIT {0} OFFSET {1}"

       Sqlite: "SELECT * LIMIT {0} OFFSET {1}"

       Firebird: "SELECT FIRST {0} SKIP {1} *"

       DB2: null -- Not Supported

       VistaDB: "SELECT TOP {2}, {0} *"

 

  我现在用的是ORM工具,他支持对常见的各种数据库分页操作。也支持自己写分页扩展。

 

关于分页的方案

1UI分页,数据库取出所有数据

   这样做的缺点显而易见,不论界面上显示多少,你都是把所有数据都去出来的,至少在内存里面。这个效率的损失就相当严重。

   最经典的DataGrid(.net1.1)的用法就是这样:设置可以分页的属性,取出数据,然后绑定到控件。这样做,其实就是一种全部依赖UI分页的做法

   当然,在DataGrid里面还支持另外一种实现,就是使用虚拟的分页,VirtualPageNumber。自己取出数据有多少页,然后取出其中一页数具进行绑定。这样做就避免了这个缺陷。

   遗憾的是在.net2.o里面,GridView反而没有了这个实现(稍后再说这个问题)

2UI只显示其中一页,数据库实现分页

   这个就是刚才上边说的DataGrid实现分页的第二种方法。优点是,效率大有提高,特别是数据量比较大的时候。

 

数据分页的简单实现

有了上边的准备,我们来说一下如何实现分页。

1UI控件的选择

   由于RepeaterDataList本身不支持分页,所以我们如果选择他的话,注定了要自己实现分页了。

我们选择方案当然是第二种分页:数据库分页和界面分页结合的方式。

首先,我们要做的就是为提供Pager. 最简单的,放置几个LinkButtom(<<,<,xx of xxx,>,>>)。这些用来触发命令

                  <asp:LinkButton ID="lb_first" runat="server" OnClick="lb_first_Click"><<</asp:LinkButton>

                  <asp:LinkButton ID="lb_prv" runat="server" OnClick="lb_prv_Click"><</asp:LinkButton>

                  <asp:Label ID="lbl_pager" runat="server" Text="0 of 0"></asp:Label>

                  <asp:LinkButton ID="lb_next" runat="server" OnClick="lb_next_Click">></asp:LinkButton>

              <asp:LinkButton ID="lb_last" runat="server" OnClick="lb_last_Click">>></asp:LinkButton>

然后让你的控件可以识别这些属性:TotalPageCount;CurrentPageIndex,PageSize

剩下的就是写程序每次绑定一下数据了。

   

 1 protected void lb_first_Click(object sender, EventArgs e)
 2    {
 3        this.CurrentPageIndex = 1;
 4        this.BindInspection();
 5    }

 6    protected void lb_prv_Click(object sender, EventArgs e)
 7    {
 8        this.CurrentPageIndex--;
 9        this.BindInspection();
10    }

11    protected void lb_next_Click(object sender, EventArgs e)
12    {
13        this.CurrentPageIndex++;
14        this.BindInspection();
15    }

16    protected void lb_last_Click(object sender, EventArgs e)
17    {
18        this.CurrentPageIndex = this.TotalPageCount;
19        this.BindInspection();
20    }

21   //property
22   private int CurrentPageIndex
23    {
24        get
25        {
26            if (Session["CurrentPageIndex"== null)
27                Session["CurrentPageIndex"= 1;
28            return Convert.ToInt32(Session["CurrentPageIndex"]);
29        }

30        set { Session["CurrentPageIndex"= value; }
31    }

32    private int TotalPageCount
33    {
34        get
35        {
36            if (Session["TotalPageCount"== null)
37                Session["TotalPageCount"= 1;
38            return Convert.ToInt32(Session["TotalPageCount"]);
39        }

40        set { Session["TotalPageCount"= value; }
41    }

42



    简单的绑定代码:

    

 1          //bind it here
 2
 3          //set property
 4           this.TotalPageCount = ps.PageCount;
 5 
 6
 7           if (this.CurrentPageIndex >= this.TotalPageCount)
 8                this.lb_next.Enabled = false;
 9            else
10                this.lb_next.Enabled = true;
11            if (this.CurrentPageIndex <= 1)
12                this.lb_prv.Enabled = false;
13            else
14                this.lb_prv.Enabled = true;
15            this.lbl_pager.Text = String.Format("{0} of {1}"this.CurrentPageIndex.ToString(), this.TotalPageCount.ToString());
16
17

 

如果你选择了GridView.没有关系,依然可以使用这种方案。

但是,也有其他选择,那就是使用DataSource控件。我不喜欢用SqlDataSource(觉得把逻辑层高的不清不楚),我一般使用ObjectDataSource.

这里有详细的使用方法:Asp.net2.0:如何使用ObjectDataSource(配合ORMhttp://www.cnblogs.com/dlwang2002/archive/2006/06/11/422991.

2:选择适当的数据操作层,用好数据库分页

  刚才提到了,我用的是一个ORM,它提供了这些实现,非常好用。

 

当然,终极解决方法是:做好你的逻辑,不要让每次取出的数据量过大。

 

Feedback

#1楼    回复  引用  查看    

2007-01-10 15:26 by kiler      
UI分页其实是一种非常糟糕的做法,强烈建议不要使用。
以datagird为例子
数据量比较大的时候,由于读出数据比较多占用了大量的系统内存,性能底下。
数据量不是很大的时候,由于viewstate体积比较大,导致页面回发速度慢,性能还是很差。

#2楼    回复  引用    

2007-01-10 15:50 by Richard[匿名] [未注册用户]
可以通过对于查询的数据取Top值来进行分页,效率要比用DataGrid/GridView高一些.

#3楼    回复  引用    

2007-01-10 15:51 by Richard[匿名] [未注册用户]
这种东西也写,唉

#4楼 [楼主]   回复  引用  查看    

2007-01-10 15:55 by 随心所欲      
@kiler
是的,很不效率。
但是这是一种可以快速实现的方法,在一些简单处理的地方(数据量也不大)就可以用。

#5楼 [楼主]   回复  引用  查看    

2007-01-10 15:59 by 随心所欲      
@Richard[匿名]
可以通过对于查询的数据取Top值来进行分页,效率要比用DataGrid/GridView高一些.
-----------------------------------------

2:UI只显示其中一页,数据库实现分页

这个就是刚才上边说的DataGrid实现分页的第二种方法。优点是,效率大有提高,特别是数据量比较大的时候。

-------------------------------------------

我说得比较详细:UI分页只是一种方案,并且效率不高。我推荐的是数据库分页结合UI分页的模型。
并且,我也列举了数据库分页的例子。

你说得着一个例子只是对于sql server的,并且还不系统(可以参照我写的关于sql server数据库分页的比较)。

#6楼 [楼主]   回复  引用  查看    

2007-01-10 16:03 by 随心所欲      
@Richard[匿名]
这种东西也写,唉
----------------------------------------
您有更高水平的技术文章,可以交流。

#7楼    回复  引用  查看    

2007-01-10 16:16 by Cure      
我觉得UI分页也是可行的,在有些系统中,表中的记录不会很多,超过一定数量就会平衡到其他数据库服务器上去,这样一来,要向客户端传输的数据就不多了。下,

#8楼    回复  引用    

2007-01-10 16:19 by NoProg [未注册用户]
我还没见过一个可以跨数据库平台的分页实现,实际上用provider方式,写一个通用的,以后就不用再讨论这个问题了

#9楼 [楼主]   回复  引用  查看    

2007-01-10 16:28 by 随心所欲      
@Cure
传送到UI的数据不会多,顶多也就是那一页。
但是,从数据库取出的数据会很多,这些数据还是放在服务器内存中的,占用资源。这就是为什么UI分页方案效率比较低的原因。

数据库分页则不会,他取出的数据只有一页,需要缓存的东西少。

#10楼 [楼主]   回复  引用  查看    

2007-01-10 16:29 by 随心所欲      
@NoProg
没错。都是使用的provier。
跨数据库平台的分页技术暂时是没有的。

#11楼    回复  引用  查看    

2007-01-10 16:40 by aspnetx      
@NoProg
那么,究竟派谁去给猫挂那个铃铛,就是个问题

#12楼    回复  引用    

2007-01-10 16:43 by 周星星[匿名] [未注册用户]
文章无任何可取之处,说出你用的什么orm,提供了什么分页方法,效率及通用性如何更有价值。说话比较直,请无见怪。

#13楼 [楼主]   回复  引用  查看    

2007-01-10 16:55 by 随心所欲      
@aspnetx
已经有了这样的Provider了。我现在就在用。WilsonORM

#14楼 [楼主]   回复  引用  查看    

2007-01-10 17:01 by 随心所欲      
@周星星[匿名]
最近几天看到几篇文章,都是在作分页,但是好像都没意识到数据库分页,只是在作UI分页。所以,我写一点经验,希望对后来者有所帮助。

文章本身的价值在于你掌握的程度,如果你都知道了,对你来说就毫无价值。

我用的WilsonORM,他对常见数据库提供分页支持(sql server,oracle, my sql等),对其他数据库提供扩展(就是告诉你告诉你用什么样的语句来分页)。

#15楼 [楼主]   回复  引用  查看    

2007-01-10 17:03 by 随心所欲      
@周星星[匿名]
不同数据量需要的分页算法是不一样的,这个工具能提供的也只是一个比较通用的方法而已。至于分页效率,要看你的应用范围。

#16楼    回复  引用  查看    

2007-01-10 17:14 by kiler      
@随心所欲

传送到UI的数据不会多,顶多也就是那一页。
但是,从数据库取出的数据会很多,这些数据还是放在服务器内存中的,占用资源。这就是为什么UI分页方案效率比较低的原因。

那你就搞错了,对于DataGird和GridView的UI分页的时候,虽然当前页面只显示一页的数据,但是DataGird和GridView控件会把所有的数据以viewstate的形式存入页面。所以页面的viewstate的体积会变的很大,造成回发速度变慢。
UI分页就是在数据量比较小的时侯比如只有几百条数据的时候还是一样的差,所以我推荐任何情况下都尽可能的不要用UI分页。

#17楼    回复  引用    

2007-01-10 17:21 by mickeyooo [未注册用户]
挺误人的 

#18楼    回复  引用  查看    

2007-01-10 17:27 by adonio      
效率都不高,说实话都不太好

#19楼    回复  引用  查看    

2007-01-10 17:44 by charleschen      
我觉得UI分页不是很好,不管怎么搞都要占资源。
最多就是和数据库分页配合下把。

#20楼    回复  引用  查看    

2007-01-10 18:12 by kkding      
我早用第二种了,不过效率没有楼主说的那麽低吧。29秒,是不是看错了。
#region 分页
/// <summary>
/// 转换参数
/// </summary>
/// <param name="param"></param>
/// <returns></returns>
private SqlParameter[] CollectionToArray(SqlParameter[] param)
{
if(param == null) return null;
SqlParameter[] ps = new SqlParameter[param.Length];
param.CopyTo(ps,0);
return ps;
}
/// <summary>
/// 分页
/// </summary>
/// <param name="fields">字段</param>
/// <param name="table">表</param>
/// <param name="filter">条件</param>
/// <param name="group">group by</param>
/// <param name="sort">条件,必填</param>
/// <returns>结果集</returns>
public DataSet PageResult(string fields,string table,string filter,string group, string sort )
{
return PageResult(1,0,"",fields,table,filter,group,sort,"");
}
/// <summary>
/// 分页
/// </summary>
/// <param name="pageno">页码</param>
/// <param name="pagesize">每页多少条</param>
/// <param name="pk">主键</param>
/// <param name="fields">字段</param>
/// <param name="table">表</param>
/// <param name="filter">条件</param>
/// <param name="sort">排序</param>
/// <param name="param">参数</param>
/// <returns>结果集</returns>
public DataSet PageResult(int pageno , int pagesize ,string pk,string fields,string table
,string filter,string sort,ArrayList param)
{
SqlParameter[] sqlparams = new SqlParameter[param.Count];
param.CopyTo(sqlparams,0);
return PageResult( pageno , pagesize , pk, fields, table
, filter,"", sort,"", sqlparams);
}
/// <summary>
/// 分页
/// </summary>
/// <param name="pageno">页码</param>
/// <param name="pagesize">每页多少条</param>
/// <param name="pk">主键</param>
/// <param name="fields">字段</param>
/// <param name="table">表</param>
/// <param name="filter">条件</param>
/// <param name="sort">排序</param>
/// <param name="param">参数</param>
/// <returns>结果集</returns>
public DataSet PageResult(int pageno , int pagesize ,string pk,string fields,string table
,string filter,string sort,SqlParameter[] param)
{
return PageResult( pageno , pagesize , pk, fields, table
, filter,"", sort,"", param);
}
/// <summary>
/// 分页
/// </summary>
/// <param name="pageno">页码</param>
/// <param name="pagesize">每页多少行</param>
/// <param name="pk">主键</param>
/// <param name="fields">字段</param>
/// <param name="table">表</param>
/// <param name="filter">条件</param>
/// <param name="group">group by</param>
/// <param name="sort">排序 必填</param>
/// <param name="sum">统计</param>
/// <param name="param">参数</param>
/// <returns>结果集</returns>
public DataSet PageResult(int pageno , int pagesize ,string pk,string fields,string table
,string filter,string group,string sort,string sum,SqlParameter[] param)
{
//定义
string sortdesc = "";
string orderby = "";
string where ="";
string groupby = "";
string realfields = "";
DataSet data = new DataSet();
DataTable result;
int count = 0;
StringBuilder sql = new StringBuilder();
//组织语句
if(filter != "")
{
where = "where " + filter.ToString();
}
if(group != "")
{
groupby = "group by " + group;
//group = ClearPrefix(group);
}
if(sort == "")
{
if(pk !="" ||pk!="*")
{
sort = pk;
}

}
if(sort != "")
{
orderby = "order by " + sort;

sortdesc = "order by " + ReplaceSortDesc(ClearPrefix(sort));
sort = "order by " + ClearPrefix(sort);
}
realfields = ClearPrefix(fields);
//构建SQL
if(pagesize == 0)
{
sql.AppendFormat("select {0} from {1} {2} {3} {4} ",
fields,table,where,groupby,orderby);
#if DEBUG
ApplicationLog.WriteLog(sql.ToString());
#endif
result = ExcuteQuery( sql.ToString(),CollectionToArray(param),"pageresult");
count = result.Rows.Count;
}
else
{
StringBuilder sqlcount = new StringBuilder();
if(groupby != "")
{
//聚合
sqlcount.AppendFormat("select count(1) from (select {0} from {1} {2} {3} ) as t1_counttable",
group,table,where,groupby);
}
else
{
sqlcount.AppendFormat("select count(1) from {0} {1} {2} ",
table,where,groupby);
//非聚合
}
//求总记录数
object obj = ExecuteScalar(sqlcount.ToString(),CollectionToArray(param));
if(Convert.IsDBNull( obj))
{
count = 0;
}
else
{
count = Convert.ToInt32( obj);
}
if(count == 0)
{
sql.AppendFormat("select {0} from {1} {2} {3} {4} ",
fields,table,where,groupby,orderby);
}
else
{
//是否超出最后一页
int lastno = (count+pagesize-1)/pagesize;
//当前页有多少记录
int lastsize = pagesize;
if(pageno >=lastno)
{
pageno = lastno;
lastsize = count - pagesize * pageno+pagesize;
}
if(pageno <= 1)
{
sql.AppendFormat("select top {0} {1} from {2} {3} {4} {5}",
pagesize,fields,table,where,groupby,orderby);
}
else
{
sql.AppendFormat("select {0} from (",realfields);
sql.AppendFormat("select top {0} {1} from ",lastsize,realfields);
sql.AppendFormat("(select top {0} {1} from {2} {3} {4} {5}) as t1 ",
pageno*pagesize,fields,table,where,groupby,orderby);
sql.AppendFormat(" {0} ) as t2", sortdesc);
sql.AppendFormat(" {0} ", sort);
}
}
#if DEBUG
ApplicationLog.WriteLog(sql.ToString());
#endif
result = ExcuteQuery( sql.ToString(),CollectionToArray(param),"pageresult");
}
data.Tables.Add(result);
DataTable counttable = new DataTable("count");
counttable.Columns.Add(new DataColumn("countdata",typeof(int)));
DataRow row = counttable.NewRow();
///总记录数
row[0] = count;
counttable.Rows.Add(row);
data.Tables.Add(counttable);
//统计求和
if(sum != "")
{
StringBuilder sqlsum = new StringBuilder();
sqlsum.AppendFormat("select {0} from {1} {2}",sum,table,where);
#if DEBUG
ApplicationLog.WriteLog(sqlsum.ToString());
#endif
DataTable tablesum =
ExcuteQuery( sqlsum.ToString(),CollectionToArray(param),"sumresult");
data.Tables.Add(tablesum);
}
Close();


return data;
}

/// <summary>
/// 分页
/// </summary>
/// <param name="pageno">页码</param>
/// <param name="pagesize">每页多少条</param>
/// <param name="pk">主键</param>
/// <param name="fields">字段</param>
/// <param name="table">表</param>
/// <param name="filter">条件</param>
/// <param name="group">group by</param>
/// <param name="sort">排序</param>
/// <param name="sum">统计</param>
/// <returns>结果集</returns>
public DataSet PageResult(int pageno , int pagesize ,string pk ,string fields ,string table ,
string filter,string group, string sort , string sum)
{
return PageResult(pageno,pagesize,pk,fields,table,filter,group,sort,sum,null);
}
#endregion

#21楼 [楼主]   回复  引用  查看    

2007-01-10 18:15 by 随心所欲      
@kiler
那你就搞错了,对于DataGird和GridView的UI分页的时候,虽然当前页面只显示一页的数据,但是DataGird和GridView控件会把所有的数据以viewstate的形式存入页面。所以页面的viewstate的体积会变的很大,造成回发速度变慢。
UI分页就是在数据量比较小的时侯比如只有几百条数据的时候还是一样的差,所以我推荐任何情况下都尽可能的不要用UI分页
-------------------------------------------------
如果真是这样,那么就得坚决使用数据库分页了。
当然,很小的应用不用这么麻烦。

#22楼    回复  引用  查看    

2007-01-10 18:15 by Cure      
@随心所欲
我的意思是有的数据库设计为表的记录数是有上限的,select的时候即使不用任何Where条件,选出来的记录也就不多,这样,向客户端传送的数据量就是可以接受的了。

#23楼    回复  引用  查看    

2007-01-10 18:15 by kkding      
支持楼主用No.2
效率第一啊

#24楼 [楼主]   回复  引用  查看    

2007-01-10 18:50 by 随心所欲      
@Cure
哪里会有这样的好事?很多客户的数据或很大的,而且要保留3年!

#25楼 [楼主]   回复  引用  查看    

2007-01-10 18:52 by 随心所欲      
@kkding
在一些特定的时候,开发速度第一。
运行效率和开发速度哪个重要,要看环境。作为程序员,我们当然希望给客户最高效的软件。

#26楼 [楼主]   回复  引用  查看    

2007-01-10 18:53 by 随心所欲      
@mickeyooo
误人?怎么一说。

#27楼 [楼主]   回复  引用  查看    

2007-01-10 19:07 by 随心所欲      
@adonio
都不太好。我们能做的就是找一个更好的。希望以后友好的技术或者思想 可以解决这个问题

#28楼 [楼主]   回复  引用  查看    

2007-01-10 19:09 by 随心所欲      
@charleschen
正是如此。只有配合一下才能真正的提高一些效率。

#29楼    回复  引用  查看    

2007-01-10 20:08 by 丁丁      
http://www.cnblogs.com/ericguo/archive/2006/11/21/practice_datagrid_page.html
我写的一篇用UI分页的文章,我想指出上面很多人认识的一个误区,其实不分页整段数据是存在DataSet中,你完全可以通过Session缓存DataSet,并不会造成ViewState的负担,从运行速度来看,这个方法效率也很高,唯一问题是数据量如果真的很大的话,非常吃服务器内存。
详细可以阅读Dino Esposito的构建Web解决方案——应用ASP.NET和ADO.NET一书,第37页中间第一句话就是“为了提高性能,DataGrid控件不在视图状态中储存数据源……”

#30楼    回复  引用  查看    

2007-01-10 21:39 by Cure      
@随心所欲
正是因为数据量大才要限定表的记录数,多的数据都用批处理等方法转到别的数据库服务器上去。

#31楼 [楼主]   回复  引用  查看    

2007-01-10 21:55 by 随心所欲      
@丁丁
怕得就是会占用服务器内存,所以才出来“数据库分页”

#32楼    回复  引用  查看    

2007-01-11 00:48 by FantasySoft      
其实在J2EE的领域,都没有什么UI分页和数据库分页之分。因为把所有数据都取出来,是一件多么可怕的事情。既然显示不了那么多,还拿那么多干嘛呢?而且,总感觉分页和UI结合在一起,层次就不清晰了,虽然有控件进行封装。

#33楼    回复  引用  查看    

2007-01-11 08:36 by kiler      
@丁丁

你自己可以做一个实验,可以发现你的数据越多页面viewstate的体积越大。

DataGrid1.DataSource = dataSet11.Tables[0].DefaultView;

DataGrid在绑定数据的时候会把dataSet1也写入viewstate。这个才是影响性能的关键。

而且你这个方法还大量占用了内存。可以说没有什么优点。


#34楼    回复  引用    

2007-01-11 09:31 by ikaiser[匿名] [未注册用户]
还是比较支持使用数据分页,用多少取多少

#35楼 [楼主]   回复  引用  查看    

2007-01-11 10:43 by 随心所欲      
@Cure
但是如果做查询(报表),那不就麻烦了?
通常一个业务流程不超过3个月,但是为了统计,必须有相当的数据量。
还有历史纪录的问题,不少来。

#36楼 [楼主]   回复  引用  查看    

2007-01-11 10:46 by 随心所欲      
@FantasySoft
在php,asp里面也没有UI分页,因为分页完全需要你自己去做。
Asp.net提供了现成的控件,稍加利用就可以,可以提供开发速度。

#37楼    回复  引用  查看    

2007-01-11 11:13 by 丁丁      
@kiler
“你的数据越多viewstate的体积越大”,我需要补充一点,页面数据越多,viewstate体积越大,请不要漏了“页面”两个字!

另外,为慎重起见,我做了实验,“DataGrid在绑定数据的时候会把dataSet1也写入viewstate写入viewstate”这句话不仅语法错,意思也是不对的,DataGrid在绑定数据的时候不会把dataSet写入ViewState!微软的工程师不是傻瓜,怎么明显的傻事是不会做的,另外请阅读Dino Esposito的书,难道你会认为一本amazon评为4星,带着WinIntellect标签的书没有你权威?

#38楼    回复  引用  查看    

2007-01-11 11:15 by HardRock      
博客园怎么把这里的评论发到我的邮箱里了,我们设置对这篇文章进行监视呀,而且只是发了一个回复,还是被扔到了垃圾邮箱里。比较奇怪

#39楼 [楼主]   回复  引用  查看    

2007-01-11 11:41 by 随心所欲      
@丁丁
讨论技术,莫做其他评论,呵呵,我们是“以技会友”。

#40楼    回复  引用    

2007-01-11 11:49 by sunriseyuen [未注册用户]
两种情况,一种只是察看的,另外一种必需全部出来的,比如打印统计报表之类的,第一种情况是不需要全部拿出来的,那100几十条数据已经够了。

#41楼 [楼主]   回复  引用&nb