DataGrid基于Access的快速分页法
撰文/ 黎波
DataGrid是一个功能非常强大的ASP.NET Web服务器端控件,它除了能够方便地按各种方式格式化显示表格中的数据,还可以对表格中的数据进行动态的排序、编辑和分页。使Web开发人员从繁琐的代码中解放。实现DataGrid的分页功能一直是很多初学ASP.NET的人感到棘手的问题,特别是自定义分页功能,实现方法多种多样,非常灵活。本文将向大家介绍一种DataGird控件在Access数据库下的快速分页法,帮助初学者掌握DataGrid的分页技术。
目前的分页方法
DataGrid内建的分页方法是使用诸如“SELECT * FROM <TABLE>”的SQL语句从数据库表中取出所有的记录到DataSet中,DataGrid控件绑定到该DataSet之后,它的自动分页功能会帮你从该DataSet中筛选出当前分页的数据并显示出来,其他没有用的数据将被丢弃。
还有一种方法是使用自定义分页功能,先将DataGrid的AllowCustomPaging属性设置为True,再利用DataAdapter的Fill方法将数据的筛选工作提前到填充DataSet时,而不是让DataGrid帮你筛选:
public int Fill ( DataSet dataSet, //要填充的 DataSet。 int startRecord, //从其开始的从零开始的记录号。 int maxRecords, //要检索的最大记录数。 string srcTable //用于表映射的源表的名称。 ); |
该方法首先将来自查询处的结果填充到DataSet中,再将不需要显示的数据丢弃。当然,自定义分页功能需要完成的事情还不止这些,本文将在后面详细介绍。
以上两种方法的工作原理都是先从数据库中取出所有的记录,然后筛选出有用的数据显示出来。可见,两种方法的效率基本上是一致的,因为它们在数据访问阶段并没有采取有效的措施来减少Access对磁盘的访问次数。对于小数量的记录,这种开销可能是比较小的,如果针对大量数据的分页,开销将会非常巨大,从而导致分页的速度非常的慢。换句话说,就算每个DataGrid分页面要显示的数据只是一个拥有几万条记录的数据库表的其中10条,每次DataGrid进行分页时还是要从该表中取出所有的记录。
很多人已经意识到了这个问题,并提出了解决方法:用自定义分页,每次只从数据库中取出要显示的数据。这样,我们需要在SQL语句上下功夫了。由于Access不支持真正的存储过程,在编写分页算法上就没有SQL Server那么自由了。SQL Server可以在存储过程中利用临时表来实现高效率的分页算法,受到了广泛的采用。而对于Access,我们必须想办法在一条SQL语句内实现最高效的算法。
用一条SQL语句取得某段数据的方法有好几种。算法不同,效率也就不同。我经过粗略的测试,发现效率最差的SQL语句执行时耗费的时间大概是效率最高的SQL语句的3倍!而且这个数值会随着记录总数的增加而增加。下面将介绍其中两条常用的SQL语句。
为了方便接下来的讨论,我们先约定如下:
变量 | 说明 | 变量 | 说明 |
@PageSize | 每页显示的记录总数 | @MiddleIndex | 中间页的索引 |
@PageCount | 分页总数 | @LastIndex | 最后一页的索引 |
@RecordCount | 数据表的记录总数 | @TableName | 数据库表名称 |
@PageIndex | 当前页的索引 | @PrimaryKey | 主键字段名称 |
@FirstIndex | 第一页的索引 | @QueryFields | 要查询的字段集 |
变量 | 定义 |
@PageCount | (int)Math.Ceiling((double)@RecordCount / @PageSize) |
@FirstIndex | 0 |
@LastIndex | @PageCount – 1 |
@MiddleIndex | (int)Math.Ceiling((double)@PageCount / 2) – 1 |
先让我们看看效率最差的SQL语句:
SELECT TOP @PageSize * FROM @TableName WHERE @PrimaryKey NOT IN ( SELECT TOP @PageSize*@PageIndex @PrimaryKey FROM @TableName ORDER BY @PrimaryKey ASC ) ORDER BY @PrimaryKey ASC |
这条SQL语句慢就慢在NOT IN这里,主SELECT语句遍历的每个@PrimaryKey的值都要跟子SELECT语句的结果集中的每一个@PrimaryKey的值进行比较,这样时间复杂度非常大。这里不得不提醒一下大家,平时编写SQL语句时应该尽量避免使用NOT IN语句,因为它往往会增加整个SQL语句的时间复杂度。
另一种是使用了两个TOP和三个ORDER BY的SQL语句,如下所示:
SELECT * FROM ( SELECT TOP @PageSize * FROM ( SELECT TOP @PageSize*(@PageIndex+1) * FROM @TableName ORDER BY @PrimaryKey ASC ) TableA ORDER BY @PrimaryKey DESC ) TableB ORDER BY @PrimaryKey ASC |
这条SQL语句空间复杂度比较大。如果要显示的分页面刚好是最后一页,那么它的效率比直接SELECT出所有的记录还要低。因此,对于分页算法,我们还应该具体情况具体分析,不能一概而论。下面将简单介绍一下相关概念,如果您对主键和索引非常熟悉,可以直接跳过。
有关主键和索引的概念
在 ACCESS中,一个表的主键(PRIMARY KEY,又称主索引)必然是唯一索引(UNIQUE INDEX),它的值是不会重复的。除此之外,索引依据索引列的值进行排序,每个索引记录包含着一个指向它所引用的数据行的指针,这对ORDER BY的执行非常有帮助。我们可以利用主键这两个特点来实现对某条记录的定位,从而快速地取出某个分页上要显示的记录。
举个例子,假设主键字段为INTEGER型,在数据库表中,记录的索引已经按主键字段的值升序排好(默认情况下),那么主键字段值为“11”的记录的索引,肯定刚好在值为“12”的记录的索引前面(假设数据库表中存在主键的值为“12”的记录)。如果主键字段不具备UNIQUE约束,数据库表中将有可能存在两个或两个以上主键字段的值为“11”的记录,这样就无法确定这些记录之间的前后位置了。
下面就让我们看看如何利用主键来进行数据的分段查询吧。
快速分页法的原理
其实该分页法是从其他方法衍生而来的。本人对原来的方法认真地分析,发现通过优化和改进可以非常有效地提高它的效率。原算法本身效率很高,但缺乏对具体问题的具体分析。同一个分页算法,可能在取第一页的数据时效率非常高,但是在取最后一页的数据时可能反而效率更低。
经过分析,我们可以把分页算法的效率状态分为四种情况:
(1)@PageIndex <= @FirstIndex
(2)@FirstIndex < @PageIndex <= @MiddleIndex
(3)@MiddleIndex < @PageIndex < @LastIndex
(4)@PageIndex >= @LastIndex
状态(1)和(4)分别表示第一页和最后一页。它们属于特殊情况,我们不必对其使用特殊算法,直接用TOP就可以解决了,不然会把问题复杂化,反而降低了效率。对于剩下的两种状态,如果分页总数为偶数,我们可以看作是从数据库表中删掉第一页和最后一页的记录,再把剩下的按前后位置平分为两部分,即前面的一部分,也就是状态(2),后面的为另一部分,也就是状态(3);如果分页总数为奇数,则属于中间页面的记录归于前面的部分。这四种状态分别对应着四组SQL语句,每组SQL语句由升序和降序两条SQL语句组成。
下面是一个数据库表,左边第一列是虚拟的,不属于该数据库表结构的一部分,它表示相应记录所在的分页索引。该表将用于接下来的SQL语句的举例中:
PageIndex | ItemId | ProductId | Price |
0 | 001 | 0011 | $12 |
002 | 0011 | $13 | |
1 | 003 | 0012 | $13 |
004 | 0012 | $11 | |
2 | 005 | 0013 | $14 |
006 | 0013 | $12 | |
3 | 007 | 0011 | $13 |
008 | 0012 | $15 | |
4 | 009 | 0013 | $12 |
010 | 0013 | $11 |
由表可得:@PageSize = 2,@RecordCount = 10,@PageCount = 5
升序的SQL语句
(1)@PageIndex <= @FirstIndex
取第一页的数据是再简单不过了,我们只要用TOP @PageSize就可以取出第一页要显示的记录了。
SELECT TOP @PageSize @QueryFields FROM @TableName WHERE @Condition ORDER BY @PrimaryKey ASC |
(2)@FirstIndex < @PageIndex <= @MiddleIndex
把取数据表前半部分记录和取后半部分记录的SQL语句分开写,可以有效地改善性能。后面我再详细解释这个原因。现在看看取前半部分记录的SQL语句。先取出当前页之前的所有记录的主键值,再从中选出最大值,然后取出主键值大于该最大值的前@PageSize条记录。这里@PrimaryKey的数据类型可以不是INTEGER类型,CHAR、VARCHAR等其他类型照样可以。
SELECT TOP @PageSize @QueryFields FROM @TableName WHERE @PrimaryKey > ( SELECT MAX(@PrimaryKey) FROM ( SELECT TOP @PageSize*@PageIndex @PrimaryKey FROM @TableName WHERE @Condition ORDER BY @PrimaryKey ASC ) TableA ) WHERE @Condition ORDER BY @PrimaryKey ASC |
例如:@PageIndex=1,红-->黄-->蓝
(3)@MiddleIndex < @PageIndex < @LastIndex
接下来看看取数据库表中后半部分记录的SQL语句。该语句跟前面的语句算法的原理是一样的,只是方法稍微不同。先取出当前页之后的所有记录的主键值,再从中选出最小值,然后取出主键值小于该最小值的前@PageSize条记录。
SELECT * FROM ( SELECT TOP @PageSize @QueryFields FROM @TableName WHERE @PrimaryKey < ( SELECT MIN(@PrimaryKey) FROM ( SELECT TOP (@RecordCount-@PageSize*(@PageIndex+1)) @PrimaryKey FROM @TableName WHERE @Condition ORDER BY @PrimaryKey DESC ) TableA ) WHERE @Condition ORDER BY @PrimaryKey DESC ) TableB ORDER BY @PrimaryKey ASC |
之所以把取数据表前半部分记录和取后半部分记录的SQL语句分开写,是因为使用取前半部分记录的SQL语句时,当前页前面的记录数目随页数递增,而我们还要从这些记录中取出它们的主键字段的值再从中选出最大值。这样一来,分页速度将随着页数的增加而减慢。因此我没有这样做,而是在当前页索引大于中间页索引时(@MiddleIndex < @PageIndex)选用了分页速度随着页数的增加而加快的算法。由此可见,假设把所有分页面划分为前面、中间和后面三部分,则最前面和最后面的分页速度最快,最中间的分页速度最慢。
例如:@PageIndex=3,红 --> 黄 --> 蓝
(4)@PageIndex >= @LastIndex
取最后一页的记录可以简单地使用类似状态(1)的做法:
SELECT * FROM ( SELECT TOP @PageSize @QueryFields FROM @TableName WHERE @Condition ORDER BY @PrimaryKey DESC ) TableA ORDER BY @PrimaryKey ASC |
不过,这样产生的最后一页不一定是实际意义上的最后一页。因为最后一页的记录数未必刚好跟@PageSize相等,而上面的SQL语句是直接取得倒数的@PageSize条记录。如果想要精确地取得最后一页的记录,应该在先计算出该页的记录数,作为TOP语句的条件:
SELECT * FROM ( SELECT TOP (@RecordCount-@PageSize*@LastIndex) @QueryFields FROM @TableName WHERE @Condition ORDER BY @PrimaryKey DESC ) TableA ORDER BY @PrimaryKey ASC |
降序的SQL语句
降序的SQL语句跟升序的大同小异,这里就不在罗嗦了J
(1)@PageIndex <= @FirstIndex
SELECT TOP @PageSize @QueryFields FROM @TableName WHERE @Condition ORDER BY @PrimaryKey DESC |
(2)@FirstIndex < @PageIndex <= @MiddleIndex
SELECT TOP @PageSize @QueryFields FROM @TableName WHERE @PrimaryKey < ( SELECT MIN(@PrimaryKey) FROM ( SELECT TOP @PageSize*@PageIndex @PrimaryKey FROM @TableName WHERE @Condition ORDER BY @PrimaryKey DESC ) TableA ) WHERE @Condition ORDER BY @PrimaryKey DESC |
(3)@MiddleIndex < @PageIndex < @LastIndex
SELECT * FROM ( SELECT TOP @PageSize @QueryFields FROM @TableName WHERE @PrimaryKey > ( SELECT MAX(@PrimaryKey) FROM ( SELECT TOP (@RecordCount-@PageSize*(@PageIndex+1)) @PrimaryKey FROM @TableName WHERE @Condition ORDER BY @PrimaryKey ASC ) TableA ) WHERE @Condition ORDER BY @PrimaryKey ASC ) TableB ORDER BY @PrimaryKey DESC |
(4)@PageIndex >= @LastIndex
SELECT * FROM ( SELECT TOP (@RecordCount-@PageSize*@LastIndex) @QueryFields FROM @TableName WHERE @Condition ORDER BY @PrimaryKey ASC ) TableA ORDER BY @PrimaryKey DESC |
如何动态产生上述的SQL语句?
看了上面的SQL语句之后,相信大家已经基本明白该分页法的原理了。下面,我们将要设计一个动态生成SQL语句的类FastPaging。该类有一个公有静态方法,它根据您给出的条件动态生成SQL语句,作为方法的返回值。
using System.Text;
namespace Paging
{
/// <summary>
/// FastPaging 的摘要说明。
/// </summary>
public class FastPaging
{
private FastPaging()
{
}
/// <summary>
/// 获取根据指定字段排序并分页查询的 SELECT 语句。
/// </summary>
/// <param name="pageSize"> 每页要显示的记录的数目。 </param>
/// <param name="pageIndex"> 要显示的页的索引。 </param>
/// <param name="recordCount"> 数据表中的记录总数。 </param>
/// <param name="tableName"> 要查询的数据表。 </param>
/// <param name="queryFields"> 要查询的字段。 </param>
/// <param name="primaryKey"> 主键字段。 </param>
/// <param name="ascending"> 是否为升序排列。 </param>
/// <param name="condition"> 查询的筛选条件。 </param>
/// <returns> 返回排序并分页查询的 SELECT 语句。 </returns>
public static String Paging(
int pageSize,
int pageIndex,
int recordCount,
String tableName,
String queryFields,
String primaryKey,
bool ascending,
String condition)
{
#region 实现
StringBuilder sb = new StringBuilder();
int pageCount = GetPageCount(recordCount, pageSize); // 分页的总数
int middleIndex = GetMidPageIndex(pageCount); // 中间页的索引
int firstIndex = 0 ; // 第一页的索引
int lastIndex = pageCount - 1 ; // 最后一页的索引
#region @PageIndex <= @FirstIndex
if (pageIndex <= firstIndex)
{
sb.Append( " SELECT TOP " ).Append(pageSize).Append( " " ).Append(queryFields)
.Append( " FROM " ).Append(tableName);
if (condition != String.Empty)
sb.Append( " WHERE " ).Append(condition);
sb.Append( " ORDER BY " ).Append(primaryKey).Append( " " )
.Append(GetSortType(ascending));
}
#endregion
#region @FirstIndex < @PageIndex <= @MiddleIndex
else if (pageIndex > firstIndex && pageIndex <= middleIndex)
{
sb.Append( " SELECT TOP " ).Append(pageSize).Append( " " ).Append(queryFields)
.Append( " FROM " ).Append(tableName)
.Append( " WHERE " ).Append(primaryKey);
if (ascending)
sb.Append( " > ( " ).Append( " SELECT MAX( " );
else
sb.Append( " < ( " ).Append( " SELECT MIN( " );
sb.Append(primaryKey).Append( " ) FROM ( SELECT TOP " )
.Append(pageSize * pageIndex).Append( " " ).Append(primaryKey)
.Append( " FROM " ).Append(tableName);
if (condition != String.Empty)
sb.Append( " WHERE " ).Append(condition);
sb.Append( " ORDER BY " ).Append(primaryKey).Append( " " )
.Append(GetSortType(ascending))
.Append( " ) TableA ) " );
if (condition != String.Empty)
sb.Append( " AND " ).Append(condition);
sb.Append( " ORDER BY " ).Append(primaryKey).Append( " " )
.Append(GetSortType(ascending));
}
#endregion
#region @MiddleIndex < @PageIndex < @LastIndex
else if (pageIndex > middleIndex && pageIndex < lastIndex)
{
sb.Append( " SELECT * FROM ( SELECT TOP " )
.Append(pageSize).Append( " " ).Append(queryFields)
.Append( " FROM " ).Append(tableName)
.Append( " WHERE " ).Append(primaryKey);
if (ascending)
sb.Append( " < ( " ).Append( " SELECT MIN( " );
else
sb.Append( " > ( " ).Append( " SELECT MAX( " );
sb.Append(primaryKey).Append( " ) FROM ( SELECT TOP " )
.Append(recordCount - pageSize * (pageIndex + 1 )).Append( " " ).Append(primaryKey)
.Append( " FROM " ).Append(tableName);
if (condition != String.Empty)
sb.Append( " WHERE " ).Append(condition);
sb.Append( " ORDER BY " ).Append(primaryKey).Append( " " )
.Append(GetSortType( ! ascending))
.Append( " ) TableA ) " );
if (condition != String.Empty)
sb.Append( " AND " ).Append(condition);
sb.Append( " ORDER BY " ).Append(primaryKey).Append( " " )
.Append(GetSortType( ! ascending))
.Append( " ) TableB ORDER BY " ).Append(primaryKey).Append( " " )
.Append(GetSortType(ascending));
}
#endregion
#region @PageIndex >= @LastIndex
else if (pageIndex >= lastIndex)
{
sb.Append( " SELECT * FROM ( SELECT TOP " ).Append(recordCount - pageSize * lastIndex)
.Append( " " ).Append(queryFields)
.Append( " FROM " ).Append(tableName);
if (condition != String.Empty)
sb.Append( " WHERE " ).Append(condition);
sb.Append( " ORDER BY " ).Append(primaryKey).Append( " " )
.Append(GetSortType( ! ascending))
.Append( " ) TableA ORDER BY " ).Append(primaryKey).Append( " " )
.Append(GetSortType(ascending));
}
#endregion
return sb.ToString();
#endregion
}
/// <summary>
/// 获取根据指定字段排序并分页查询的 SELECT 语句。
/// </summary>
/// <param name="pageSize"> 每页要显示的记录的数目。 </param>
/// <param name="pageIndex"> 要显示的页的索引。 </param>
/// <param name="recordCount"> 数据表中的记录总数。 </param>
/// <param name="tableName"> 要查询的数据表。 </param>
/// <param name="queryFields"> 要查询的字段。 </param>
/// <param name="primaryKey"> 主键字段。 </param>
public static String Paging(
int pageSize,
int pageIndex,
int recordCount,
String tableName,
String queryFields,
String primaryKey)
{
return Paging(pageSize, pageIndex, recordCount, tableName, queryFields, primaryKey,
true , String.Empty);
}
/// <summary>
/// 获取根据指定字段排序并分页查询的 SELECT 语句。
/// </summary>
/// <param name="pageSize"> 每页要显示的记录的数目。 </param>
/// <param name="pageIndex"> 要显示的页的索引。 </param>
/// <param name="recordCount"> 数据表中的记录总数。 </param>
/// <param name="tableName"> 要查询的数据表。 </param>
/// <param name="queryFields"> 要查询的字段。 </param>
/// <param name="primaryKey"> 主键字段。 </param>
/// <param name="ascending"> 是否为升序排列。 </param>
/// <returns> 返回排序并分页查询的 SELECT 语句。 </returns>
public static String Paging(
int pageSize,
int pageIndex,
int recordCount,
String tableName,
String queryFields,
String primaryKey,
bool ascending)
{
return Paging(pageSize, pageIndex, recordCount, tableName, queryFields, primaryKey,
ascending, String.Empty);
}
/// <summary>
/// 获取根据指定字段排序并分页查询的 SELECT 语句。
/// </summary>
/// <param name="pageSize"> 每页要显示的记录的数目。 </param>
/// <param name="pageIndex"> 要显示的页的索引。 </param>
/// <param name="recordCount"> 数据表中的记录总数。 </param>
/// <param name="tableName"> 要查询的数据表。 </param>
/// <param name="queryFields"> 要查询的字段。 </param>
/// <param name="primaryKey"> 主键字段。 </param>
/// <param name="condition"> 查询的筛选条件。 </param>
/// <returns> 返回排序并分页查询的 SELECT 语句。 </returns>
public static String Paging(
int pageSize,
int pageIndex,
int recordCount,
String tableName,
String queryFields,
String primaryKey,
String condition)
{
return Paging(pageSize, pageIndex, recordCount, tableName, queryFields, primaryKey,
true , condition);
}
/// <summary>
/// 计算分页数。
/// </summary>
/// <param name="recordCount"> 表中得记录总数。 </param>
/// <param name="pageSize"> 每页显示的记录数。 </param>
/// <returns> 分页数。 </returns>
public static int GetPageCount( int recordCount, int pageSize)
{
return ( int )Math.Ceiling(( double )recordCount / pageSize);
}
/// <summary>
/// 计算中间页的页索引。
/// </summary>
/// <param name="pageCount"> 分页数。 </param>
/// <returns> 中间页的页索引。 </returns>
public static int GetMidPageIndex( int pageCount)
{
return ( int )Math.Ceiling(( double )pageCount / 2 ) - 1 ;
}
/// <summary>
/// 获取排序的方式("ASC" 表示升序,"DESC" 表示降序)。
/// </summary>
/// <param name="ascending"> 是否为升序。 </param>
/// <returns> 排序的方式("ASC" 表示升序,"DESC" 表示降序)。 </returns>
public static String GetSortType( bool ascending)
{
return (ascending ? " ASC " : " DESC " );
}
/// <summary>
/// 获取一个布尔值,该值指示排序的方式是否为升序。
/// </summary>
/// <param name="orderType"> 排序的方式("ASC" 表示升序,"DESC" 表示降序)。 </param>
/// <returns> "ASC"则为 true;"DESC"则为 false;其它的为 true。 </returns>
public static bool IsAscending(String orderType)
{
return ((orderType.ToUpper() == " DESC " ) ? false : true );
}
}
}
让DataGrid工作起来
有了上面的类,实现分页的工作就简单多了。首先,我们要将DataGrid的AllowPaging属性和AllowCustomPaging属性为True,除此之外,为了体现出升序和降序的功能,还需要将AllowSorting属性也设置为True。然后在每次分页时,我们需要产生一个OleDbDataReader对象或DataView对象绑定到DataGrid,作为DataGrid的数据源。这里需要用FastPaging类的Paging方法根据条件产生一个SQL语句,并赋给OleDbCommand对象的CommandText属性:
cmd.CommandText = FastPaging.Paging( DataGrid1.PageSize, (int)ViewState["CurrentPageIndex"], DataGrid1.VirtualItemCount, "Items", "ItemId, ProductId, Price", "ItemId", FastPaging.IsAscending(OrderType), "" ); |
在上面的程序段中,ViewState["CurrentPageIndex"]的值在DataGrid的Page事件处理程序中被更新为e.NewPageIndex。为了方便处理ViewState的空值,最好把对ViewState["CurrentPageIndex"]的存取操作和空值判断封装在一个属性里。DataGrid1. VirtualItemCount应该设置为数据库表中的记录总数。DataGrid通过它和PageSize属性可以虚拟出DataGrid的分页数。VirtualItemCount的值是在Page的Load事件处理程序中被设置的,而该值的大小需要经过一次数据库访问才能得到。为了提高性能,可以只在第一次加载页面的时候设置该值。
实现快速分页
我使用Access自带的Northwind中文数据库的“订单明细”表作为例子,不过我在该表添加了一个名为“Id”的字段,数据类型为“自动编号”,并把该表命名为“订单明细表”。
FastPaging_DataSet.aspx
--------------------------------------------------------------------------------------
Inherits = " Paging.FastPaging_DataSet " EnableSessionState = " False " EnableViewState = " True "
EnableViewStateMac = " False " %>
<! DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
< html >
< head >
< title > DataGrid + DataReader 自定义分页 </ title >
< meta content ="Microsoft Visual Studio .NET 7.1" name ="GENERATOR" >
< meta content ="C#" name ="CODE_LANGUAGE" >
< meta content ="JavaScript" name ="vs_defaultClientScript" >
< meta content ="http://schemas.microsoft.com/intellisense/ie5" name ="vs_targetSchema" >
</ head >
< body >
< form id ="Form1" runat ="server" >
< asp:DataGrid ID ="DataGrid1" runat ="server" BorderWidth ="1px" BorderColor ="Black"
Font-Size ="12pt" AlternatingItemStyle-BackColor ="#eeeeee" HeaderStyle-BackColor ="#aaaadd"
PagerStyle-HorizontalAlign ="Right" CellPadding ="3" AllowPaging ="True" AllowCustomPaging ="True"
AutoGenerateColumns ="False" OnPageIndexChanged ="MyDataGrid_Page" PageSize ="15"
AllowSorting ="True" OnSortCommand ="DataGrid1_SortCommand" >
< AlternatingItemStyle BackColor ="#EEEEEE" ></ AlternatingItemStyle >
< ItemStyle Font-Size ="Smaller" BorderWidth ="22px" ></ ItemStyle >
< HeaderStyle BackColor ="#AAAADD" ></ HeaderStyle >
< Columns >
< asp:BoundColumn DataField ="ID" SortExpression ="ID" HeaderText ="ID" ></ asp:BoundColumn >
< asp:BoundColumn DataField ="订单ID" HeaderText ="订单ID" ></ asp:BoundColumn >
< asp:BoundColumn DataField ="产品ID" HeaderText ="产品ID" ></ asp:BoundColumn >
< asp:BoundColumn DataField ="单价" HeaderText ="单价" ></ asp:BoundColumn >
< asp:BoundColumn DataField ="数量" HeaderText ="数量" ></ asp:BoundColumn >
< asp:BoundColumn DataField ="折扣" HeaderText ="折扣" ></ asp:BoundColumn >
</ Columns >
< PagerStyle Font-Names ="VerDana" Font-Bold ="True" HorizontalAlign ="Right" ForeColor ="Coral"
Mode ="NumericPages" ></ PagerStyle >
</ asp:DataGrid ></ form >
</ body >
</ html >
FastPaging_DataSet.aspx.cs
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Web;
using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
using System.Data.OleDb;
using System.Text;
namespace Paging
{
public class FastPaging_DataSet : System.Web.UI.Page
{
protected System.Web.UI.WebControls.DataGrid DataGrid1;
const String QUERY_FIELDS = " * " ; // 要查询的字段
const String TABLE_NAME = " 订单明细表 " ; // 数据表名称
const String PRIMARY_KEY = " ID " ; // 主键字段
const String DEF_ORDER_TYPE = " ASC " ; // 默认排序方式
const String SEC_ORDER_TYPE = " DESC " ; // 可选排序方式
const String CONDITION = " 产品ID='AV-CB-1' " ;
OleDbConnection conn;
OleDbCommand cmd;
OleDbDataAdapter da;
#region 属性
#region CurrentPageIndex
/// <summary>
/// 获取或设置当前页的索引。
/// </summary>
public int CurrentPageIndex
{
get { return ( int )ViewState[ " CurrentPageIndex " ]; }
set { ViewState[ " CurrentPageIndex " ] = value; }
}
#endregion
#region OrderType
/// <summary>
/// 获取排序的方式:升序(ASC)或降序(DESC)。
/// </summary>
public String OrderType
{
get
{
String orderType = DEF_ORDER_TYPE;
if (ViewState[ " OrderType " ] != null )
{
orderType = (String)ViewState[ " OrderType " ];
if (orderType != SEC_ORDER_TYPE)
orderType = DEF_ORDER_TYPE;
}
return orderType;
}
set { ViewState[ " OrderType " ] = value.ToUpper(); }
}
#endregion
#endregion
private void Page_Load( object sender, System.EventArgs e)
{
#region 实现
String strConn = " PROVIDER=Microsoft.Jet.OLEDB.4.0;Data Source= "
+ Server.MapPath( " Northwind.mdb " );
conn = new OleDbConnection(strConn);
cmd = new OleDbCommand( "" , conn);
da = new OleDbDataAdapter(cmd);
if ( ! IsPostBack)
{
// 设置用于自动计算页数的记录总数
DataGrid1.VirtualItemCount = GetRecordCount(TABLE_NAME);
CurrentPageIndex = 0 ;
BindDataGrid();
}
#endregion
}
#region Web 窗体设计器生成的代码
override protected void OnInit(EventArgs e)
{
//
// CODEGEN: 该调用是 ASP.NET Web 窗体设计器所必需的。
//
InitializeComponent();
base .OnInit(e);
}
/// <summary>
/// 设计器支持所需的方法 - 不要使用代码编辑器修改
/// 此方法的内容。
/// </summary>
private void InitializeComponent()
{
this .Load += new System.EventHandler( this .Page_Load);
}
#endregion
private void BindDataGrid()
{
#region 实现
// 设置当前页的索引
DataGrid1.CurrentPageIndex = CurrentPageIndex;
// 设置数据源
DataGrid1.DataSource = GetDataView();
DataGrid1.DataBind();
#endregion
}
/// <summary>
/// 取得数据库表中的记录总数。
/// </summary>
/// <param name="tableName"> 数据库中的表的名称。 </param>
/// <returns> 成功则为表中记录的总数;否则为 -1。 </returns>
private int GetRecordCount( string tableName)
{
#region 实现
int count;
cmd.CommandText = " SELECT COUNT(*) AS RecordCount FROM " + tableName;
try
{
conn.Open();
count = Convert.ToInt32(cmd.ExecuteScalar());
}
catch (Exception ex)
{
Response.Write(ex.Message.ToString());
count = - 1 ;
}
finally
{
conn.Close();
}
return count;
#endregion
}
private DataView GetDataView()
{
#region 实现
int pageSize = DataGrid1.PageSize;
DataSet ds = new DataSet();
DataView dv = null ;
cmd.CommandText = FastPaging.Paging(
pageSize,
CurrentPageIndex,
DataGrid1.VirtualItemCount,
TABLE_NAME,
QUERY_FIELDS,
PRIMARY_KEY,
FastPaging.IsAscending(OrderType));
try
{
da.Fill(ds, TABLE_NAME);
dv = ds.Tables[ 0 ].DefaultView;
}
catch (Exception ex)
{
Response.Write(ex.Message.ToString());
}
return dv;
#endregion
}
protected void MyDataGrid_Page(Object sender, DataGridPageChangedEventArgs e)
{
CurrentPageIndex = e.NewPageIndex;
BindDataGrid();
}
protected void DataGrid1_SortCommand( object source, DataGridSortCommandEventArgs e)
{
#region 实现
DataGrid1.CurrentPageIndex = 0 ;
this .CurrentPageIndex = 0 ;
if (OrderType == DEF_ORDER_TYPE)
OrderType = SEC_ORDER_TYPE;
else
OrderType = DEF_ORDER_TYPE;
BindDataGrid();
#endregion
}
}
}
总结
DataGrid基于Access的快速分页法到这里就介绍完了。当然,这种方法并不能“包治百病”,可能对于您的要实现的功能,还有其它更好的方法。这就需要大家在平时工作和学习中不断总结经验,在解决实际问题时尽可能找到最有效的方法。这也是本文的方法中所贯穿的思想。