2004-7-22+ DataGrid的另类分页

我发现这个有问题啊,如果id号突然跳跃很大的话,就不行
比如73,74,75,76,112,sql查询的时候是id取一个区间,可是这样就没有办法了假设每页三条记录,而当前页正好应该是75,76,112这三条的话,112就根本取不到
目前努力解决中


DataGrid的内建分页方法是效率不高的,每次请求都必须把整个查询结果发送给Web服务器,Web服务器再把数据分成相应的页面。利用DataGrid的内建的分页方法尽管是很简单的,但是,由于Web应用的无序性特征,一个用户每次从一个页面转向另外一个页面时,DataGrid对象都被销毁并重新创建,这就意味着数据库服务器每次都必须发送全部的结果集。而自定义分页则避免了这个问题,是比DataGrid的默认分页方法更快速和更加有效的,这是因为每次请求不需要把全部的数据结果发送到Web服务器。相反,它只需要发送每个页面需要的那些数据集。例如:如果一个用户只要求100个页面中每页显示25条记录的第4页的结果集,服务器只需要发送第75-100行的数据即可,而不是1-1000行的完全数据。

DataGrid的自定义分页一直是大家讨论的焦点,我这两天也找了些例子看,不过都弄的我晕乎乎的,主要是大家都用存储过程写,而我还没有开始学……再加上最近要做的一个asp.net站也是用access做后台数据库,所以我就自己研究了这个在access下可以用的分页程序。
这个程序的不足之处在于只能够用“上一页”“下一页”“第一页”进行分页查询,对于用数字直接到某一页就无能为力了,不过好处也是很明显的,就是在传递数据的时候只传递需要的当前页的数据,而不是像DataGrid的默认分页那样一次把所有数据发过来,然后再操作。看到这里你会说,这不是DataGrid的自定义分页吗?其实大体上差不多,关键是我写的这个根本就没有用到DataGrid的分页功能,而是完全用程序在一开始的时候就把DataGrid的数据源设定成当页所需要的数据。而且这个在使用数据的时候用的是DataReader而不是DataSet,并且连ViewState也禁用了,所以效率应该也不错。因为web是无状态的,所以我觉得这样似乎也没有什么不好,呵呵。
具体应用的时候可以提供使用“上下页查询”和“数字页查询”两种办法,用户可以选一种为默认分页方法,这样在“上下页查询”时使用本分页方法(快),在“数字页查询”时就使用DataGrid的默认分页方法(慢),另外在加一个根据“关键字搜索相应数据”。这样也可以应付一般的站点了。

一不小心就说了这么一大片,下面是具体的实现方法:


基本的工作流程是:页面初次加载时显示第一页的记录,用户通过单击上下页的按钮来进行页的翻动,这两个按钮是独立于datagrid的,单击时响应事件会根据需要的页码来重新从数据库取需要的记录,然后在把记录绑定到datagrid。

重点就是怎么样来截取数据库中指定区域的记录,我们知道datareader是只度向前的,所以要想用datareader来完成这个操作不可能,那么我们只有在一开始的时候用sql查询来返回指定区域的记录了。为此需要在数据表里加一个自动增长的id字段(一般的数据表里都有这东西吧?),然后根据id的值进行查寻"select * from yourtable where id>min and id<max",其中min和max分别是当页的最小id和最大id,这样查询返回的记录当然就是当页所需的记录。

所以现在的问题就变成怎么可以获得min和max的值,因为是从当前页来进行上下页的操作,所以可以从当前页的记录所含的id来分别计算出上下页的min和max,然后在进行具体操作就可以了。需要注意的是,很多人以为可以用pagesize等来计算id数,比如说第三页的min数就应该是pagesize*3,而max数则是pagesize*+pagesize,实际上这样是错误的。由于日常的管理操作,导致id并不是顺序排下来的,而这个方法是建立在id完全顺序的基础上,所以根据行不通。

取的当前页的记录所含的id值,最方便的做法就是把id做为一个单独列放到datagrid里面,然后剩下的工作就是从datagrid控件里把特定列的特定行的内容取出来了,这个不难作到。

前面我们执意要用datareader,是因为使用DataReader比使用DataSet要快两倍以上,而为了追求性能最佳化,我们要同时设定DataGrid的EnableViewState属性为false,根据测试表明,不保存ViewState,则DataGrid会提高性能到54%。一旦我们关闭了DataGrid自己在ViewState中保存的能力,我们就必须自己编写代码来实现用户从一页导航到另一页。这正是本分页方法的主要部分 ,而每一页的min和max则可以放到ViewState中进行保存,之所以不把它们放到Session里,也是为了提高性能。

这一切都完成了,剩下的工作就是在datagrid下面加两个linkbutton,然后在按钮事件的响应中对数据进行重新获取,然后重新绑定到datagrid。
基本的程序原理都在上面了,下面是关键的代码部分

datagrid的基本设置
<asp:datagrid id="dgpaging" runat="server" PageSize="3" AutoGenerateColumns="False" EnableViewState="False">
<Columns>
<asp:BoundColumn DataField="id" HeaderText="编号"></asp:BoundColumn>
.........
</Columns>

注意这里我们设定了一个PageSize,这样在后面程序部分我们用dgpaging.PageSize就可以获得每页的大小,省去了很多麻烦。另外第一列id就是需要的编号列。

在页面首次加载时执行的程序
private void setup(int psize)
{
   string strconn="server=localhost;uid=sa;pwd=123;database=lgwin";
   SqlConnection conn=new SqlConnection(strconn);
   //查找指定数量的记录,因为是第一页,所以不需要max和min那么麻烦,只一个top就可以
   StringBuilder sql=new StringBuilder();
   sql.Append("select top ");
   sql.Append(psize);
   sql.Append(" id,named,act from ms order by id desc");
   SqlCommand cmd=new SqlCommand(sql.ToString(),conn);
   try
   {
      conn.Open();
      dgpaging.DataSource=cmd.ExecuteReader();
      dgpaging.DataBind();
   }
   catch(SqlException ex)
   {
      Response.Write(ex.Message);
   }
   finally
   {
      conn.Close();
   }
   //保存min和max
   ViewState["sid"]=dgpaging.Items[0].Cells[0].Text;
   ViewState["eid"]=dgpaging.Items[dgpaging.Items.Count-1].Cells[0].Text;
}
上面是显示第一页的程序,在这里只传回了第一页所需要的记录,另外看保存max和min的方法,min就是datagrid的第一行第一列的id值,max则是最后一行的第一列的id值。

然后就是重点:响应按钮事件的方法中的当前页的数据设定
private void setdata(int psize,string action)
{
   string strconn="server=localhost;uid=sa;pwd=123;database=lgwin";
   conn=new SqlConnection(strconn);
   cmd=new SqlCommand();
   cmd.Connection=conn;
   int sid,eid;
   StringBuilder sql=new StringBuilder();
   int iteml=dgpaging.Items.Count-1;
   //获得min
   sid=Int32.Parse(ViewState["sid"].ToString());
   //获得max
   eid=Int32.Parse(ViewState["eid"].ToString());
   if(action=="prv")
   {
      //数据查找
      sql.Append("select");
      sql.Append(" id,named,act from ms where id>");
      sql.Append(sid);
      sql.Append(" and id<=");
      sql.Append(sid+psize);

      sql.Append(" order by id desc");
      cmd.CommandText=sql.ToString();
   }
   if(action=="next")
   {
      //数据查找
      sql.Append("select");
      sql.Append(" id,named,act from ms where id<");
      sql.Append(eid);
      sql.Append(" and id>=");
      sql.Append(eid-psize);

      sql.Append(" order by id desc");
      cmd.CommandText=sql.ToString();
   }
   //绑定数据
   setuppage();
}
private void setuppage()
{
   try
   {
      conn.Open();
      dgpaging.DataSource=cmd.ExecuteReader();
      dgpaging.DataBind();
   }
   catch(SqlException ex)
   {
      Response.Write(ex.Message);
   }
   finally
   {
      conn.Close();
   }
  //保存当前页的min和max
   ViewState["sid"]=dgpaging.Items[0].Cells[0].Text;
   ViewState["eid"]=dgpaging.Items[dgpaging.Items.Count-1].Cells[0].Text;
}
重点是sql查询那一块,要想清楚id到底是在那个区间里面,否则就出错了。setdata其实就是重新进行了数据库查询并重新把数据绑定到datagrid,不过其中返回的数据是当前页需要的,注意在最后还要重新保存新的min和max。

最后的响应按钮事件的方法
//上一页
private void butprv_Click(object sender, System.EventArgs e)
{
   setdata(dgpaging.PageSize,"prv");
}
//下一页
private void butnext_Click(object sender, System.EventArgs e)
{
   setdata(dgpaging.PageSize,"next");
}
//第一页
private void butfirst_Click(object sender, System.EventArgs e)
{
   setup(dgpaging.PageSize);
}
第一页的程序实际上和初次加载时一样。根据参数进行上下页的操作。

下面是两篇参考文章,感兴趣的可以看一下:
Web Form中的Datagrid的自定义分页
利用自定义分页技术提高数据库性能

posted on 2006-06-29 09:31  Notus|南色的风  阅读(306)  评论(0编辑  收藏  举报