GridView,ASP.NET中的表格控件,和Repeater控件一样,在ASP.NET中都是很常用的两个表格控件,GridView自己也有分页功能,实现是将一张表的数据全部绑定到GridView,然后再进行分页,这就是通常我们所说的'假分页'。在应对小数据量上完全没问题,但问题往往没有那么简单,在面对大数据量的问题上,GridView自己的分页还是力不从心,严重影响效率,用户体验也不好。
要解决这个问题,实现'真分页',就要从根上去分析,为什么'假分页'速度慢,因为它一次获取的是整张表的数据,但是最后显示的只是一页,所以,如果我们只查询需要显示的一页数据(上页下页,首页尾页,跳页都只查询需要的一页数据),在绑定到GridView中,查询分页的速度会快很多,这就是所谓的'真分页'了。剩下的问题就是SQL语句去实现了。经过一番查询研究,通过存储过程实现真分页效果挺好。
存储过程
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[PROC_GridViewPage]
--@TableList Varchar(200), --查询的字段,用逗号隔开
@TableName Varchar(30), --查询的表名
--@SelectWhere Varchar(500),--查询的条件
@SelectOrderId Varchar(20), --表主键字段名
@SelectOrder Varchar(200), --排序,order by 列名
@intPageNo int, --页号
@intPageSize int, --每页显示数
@RecordCount int OUTPUT --总记录数(OUTPUT表示是存储过程输出型参数,接收方法见DAL层)
AS
declare @TmpSelect NVarchar(600) --声明变量
set nocount on --关闭计数
--获取表中记录总数
set @TmpSelect = 'select @RecordCount = count(*) from '+@TableName+' ' --可以添加查询条件+@SelectWhere
execute sp_executesql
@TmpSelect, --执行上面的sql语句
N'@RecordCount int OUTPUT' , --执行输出数据的sql语句,output出总记录数
@RecordCount OUTPUT
if (@RecordCount = 0) --如果表中没有,则返回0
return 0
/*判断页数是否正确*/
if (@intPageNo - 1) * @intPageSize > @RecordCount --页号大于总页数,返回错误
return (-1)
set nocount off--打开计数
BEGIN
set @TmpSelect = 'select top '+str(@intPageSize)+' '+ '*' +' from '+@TableName+' where '+@SelectOrderId+' not in(select top '+str((@intPageNo-1)*@intPageSize)+' '+@SelectOrderId+' from '+@TableName+' '+@SelectOrder+') '+@SelectOrder
END
execute sp_executesql @TmpSelect
return(@@rowcount)
GO
sp_executesql?和EXEC一样在存储过程中执行sql语句的命令,平时我们见到和使用的通常是EXEC,sp_executesql的不同之出在于,它提供了一个借口,支持参数的输入和输出,灵活性更好,而且sp_executesql的执行性能更好。还没有深入研究sp_executesql,既然都说比EXEC好,那就先用吧。
.NET代码实现
前台代码
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="List.aspx.cs" Inherits="_Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>GridView分页</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:GridView ID="gridView" runat="server" AllowPaging="True" Width="100%" CellPadding="3"
OnPageIndexChanging="gridView_PageIndexChanging" BorderWidth="1px" DataKeyNames="ID"
OnRowDataBound="gridView_RowDataBound" AutoGenerateColumns="false"
RowStyle-HorizontalAlign="Center" OnRowCreated="gridView_OnRowCreated" EmptyDataText="没有您查找的数据"
ShowHeaderWhenEmpty="True" PageSize="20" >
<Columns>
<asp:BoundField DataField="name" HeaderText="姓名" SortExpression="name" ItemStyle-HorizontalAlign="Center">
<asp:BoundField DataField="sex" HeaderText="性别" SortExpression="sex" ItemStyle-HorizontalAlign="Center">
</asp:BoundField>
<asp:BoundField DataField="idNumber" HeaderText="身份证号" SortExpression="idNumber"
ItemStyle-HorizontalAlign="Center">
</asp:BoundField>
</Columns>
</asp:GridView>
</div>
<asp:HyperLink ID="pagefirst" runat="server">首页</asp:HyperLink>
<asp:HyperLink ID="pageup" runat="server">上一页</asp:HyperLink>
<asp:HyperLink ID="pagedown" runat="server">下一页</asp:HyperLink>
<asp:HyperLink ID="pageend" runat="server">尾页</asp:HyperLink>
第<asp:Label ID="lbRow" runat="server" Text="Label"></asp:Label>页,
共<asp:Label ID="lbpage" runat="server" Text="Label"></asp:Label>页,
共<asp:Label ID="lbRecord" runat="server" Text="Label"></asp:Label>条记录,
转到<asp:TextBox ID="txtlink" runat="server" Width="29px"></asp:TextBox>页
<asp:LinkButton ID="link" runat="server" OnClick="link_Click" TabIndex="1">转到</asp:LinkButton>
</form>
</body>
</html>
后台代码
实体类 GridViewPageEntity.cs
这个实体类的作用是定义了存储过程中的变量:字段,表名,查询条件,主键,排序方式,页号,总记录数
public partial class GridViewPageEntity
{
public GridViewPageEntity()
{}
///定义属性中的变量
/// <summary>
/// 字段,表名,查询条件,主键,排序方式,页号,总记录数
/// </summary>
private string _Fields;
private string _TableName;
private string _SelectWhere;
private string _Primarykey;
private string _OrderBy;
private int _intPageNo;
private int _intPageSize;
private int _RecordCount;
//属性
public string Fields
{
set { _Fields = value; }
get { return _Fields; }
}
public string TableName
{
set { _TableName = value; }
get { return _TableName; }
}
public string SelectWhere
{
set { _SelectWhere = value; }
get { return _SelectWhere; }
}
public string Primarykey
{
set { _Primarykey = value; }
get { return _Primarykey; }
}
public string OrderBy
{
set { _OrderBy = value; }
get { return _OrderBy; }
}
public int intPageNo
{
set { _intPageNo = value; }
get { return _intPageNo; }
}
public int intPageSize
{
set { _intPageSize = value; }
get { return _intPageSize; }
}
public int RecordCount
{
set { _RecordCount = value; }
get { return _RecordCount; }
}
}
界面层
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack) //判断是否第一次加载页面
{
BindData();
}
}
#region 数据绑定
public void BindData()
{
private readonly GridViewPageBLL gridViewPageBll = new GridViewPageBLL(); //实例化GridViewPageBLL 业务逻辑类
PersonalFiles.Entity.GridViewPageEntity gridViewEntity = new Entity.GridViewPageEntity();//实例化GridViewPageEntity实体类
gridViewEntity.intPageNo = 1; //起始页号
gridViewEntity.intPageSize = 20; //每页显示行数
gridViewEntity.TableName = "V_XINMainTecnce"; //查询的表名(这里用的视图)
gridViewEntity.Primarykey = "pri"; //主键
gridViewEntity.OrderBy = "order by ID"; //排序方式
//获取当前页号,判断是否获取页,没有则页号为起始页
if (Request.QueryString["CurrentPage"] == null)
{
gridViewEntity.intPageNo = 1;
}
else
{
gridViewEntity.intPageNo = Int32.Parse(Request.QueryString["CurrentPage"]);
}
int sumPage; //定义总页数
int pageNo = gridViewEntity.intPageNo; //当前页数
int pageSize = gridViewEntity.intPageSize; //每页显示行数
DataTable dt = new DataTable(); //实例化DataTable
dt = gridViewPageBll.GetList(gridViewEntity); //获取数据列表
gridView.DataSource = dt; //绑定数据
gridView.DataBind();
string allcount = dt.Rows[0]["allcount"].ToString(); //获取表中数据总行数
Int32 RecordCount = int.Parse(allcount); //求出总记录数,该值是output出来的值
Int32 RowCount = (Int32)dt.Rows.Count; //求出当前页中的记录数,在最后一页不等于pagesize,
lbRecord.Text = RecordCount.ToString(); //显示总条数
lbRow.Text = pageNo.ToString(); //显示当前页数
sumPage = (Int32)RecordCount / pageSize; //计算总页数
if (RecordCount % pageSize > 0) //判断总行数是否大于每页行数,是则总页数自加1
{
sumPage = sumPage + 1;
}
lbpage.Text = sumPage.ToString(); //显示总页数
if (pageNo > 1) //判断当前页是否大于1(不是首页),判断是否可以进行首页和上一页
{
pagefirst.NavigateUrl ="List.aspx?CurrentPage=1"; //链接首页
pageup.NavigateUrl =string.Concat("List.aspx?CurrentPage=", "", pageNo - 1); //上一页操作
}
else
{
pageup.NavigateUrl ="";
pagefirst.NavigateUrl ="";
}
if (pageNo < sumPage) //判断当前页是否小于总页数,判断是否可以进行下一页和最后一页
{
pageend.NavigateUrl =string.Concat("List.aspx?CurrentPage=", "", sumPage); //最后一页操作
pagenext.NavigateUrl =string.Concat("List.aspx?CurrentPage=", "", pageNo + 1); //下一页操作
}
else
{
pagenext.NavigateUrl ="";
pageend.NavigateUrl ="";
}
}
#endregion
业务逻辑类BLL.GridViewPageBLL
/// <summary>
/// GridView分页 逻辑类
/// </summary>
public partial class GridViewPageBLL
{
private readonly MainTenanceDAL gridViewPageDal=new GridViewPageDAL();
/// <summary>
/// 获取数据BLL方法
/// </summary>
/// <param name="gridViewEntity">分页参数实体</param>
/// <returns>返回DataTable</returns>
public DataTable GetList(PersonalFiles.Entity.GridViewPageEntity gridViewEntity)
{
return gridViewPageDal.GetList(gridViewEntity);
}
}
数据访问类DAL.GridViewDAL
/// <summary>
/// 查询数据DAL方法
/// </summary>
/// <param name="gridViewEntity"></param>
/// <returns></returns>
public DataTable GetList(PersonalFiles.Entity.GridViewPageEntity gridViewEntity)
{
//存储过程
string strSql = "PROC_GridViewPage";
//添加赋值参数
SqlParameter[] paras = new SqlParameter[] {
//new SqlParameter("@TableList","ID"),
new SqlParameter("@TableName",gridViewEntity.TableName),
new SqlParameter ("@SelectOrderId",gridViewEntity.Primarykey),
new SqlParameter ("@SelectOrder",gridViewEntity.OrderBy),
new SqlParameter ("@intPageNo",gridViewEntity.intPageNo),
new SqlParameter ("@intPageSize",gridViewEntity.intPageSize),
new SqlParameter ("@RecordCount",SqlDbType.Int),
new SqlParameter ("RowCount", SqlDbType.Int)
};
//获取表中总数据量,如果不需要界面显示可以不用获取。
paras[5].Direction = ParameterDirection.Output;
//获取查询的一页的数据量,我们在界面层给@intPageSize赋的值是20,所以这里返回来的也是20
paras[6].Direction = ParameterDirection.ReturnValue;
DataSet ds = new DataSet();
//执行带参数的查询存储过程
DataTable dt = DbHelperSQL.ExecuteQuery(strSql,paras,CommandType.StoredProcedure);
string allcount = paras[5].Value.ToString();
string count = paras[6].Value.ToString();
//存储表总数据量,如果不需要界面显示可以不用获取。
dt.Columns.Add("allcount", typeof(string));
dt.Rows[0]["allcount"] = allcount;
//返回查询结果(datatable)
return dt;
}
SqlParameter类的属性Direction作用在获取或者设置一个值,该值指示参数是只可以输入,只可以输出,双向,还是存储过程返回值参数。
paras[5].Direction= ParameterDirection.Output;得到存储过程执行后的输出值
paras[6].Direction= ParameterDirection.ReturnValue;得到存储过程执行后的返回值
要实现这个获取值需要在存储过程中将参数设为OUTPUT类型:@RecordCount int OUTPUT,详看存储过程。
总结
代码实现完成了,整个过程很清楚了,不过感觉还有很多不太明白的地方,可能就是对其中的细节上理解的不太明白,也是以前没有接触过不熟悉的原因吧。不纠结了,最后能够从全局上掌握这个方法,会用了即可吧。
学习牛腩的时候用过第三方控件AspNetPager,只要引入封装的.dll就能实现真分页,原理上应该是一样的,而且它都给你封装好了,拿来用就行,怎么封装出来的,看来还要好好研究。