ASP .NET 如何在 SQL 查询层面实现分页
【编者按】本文作者为来自巴基斯坦的软件开发工程师 Aqeeel,主要介绍了在 SQL 查询层面实现 ASP.NET 应用的分页方法。
本文系 OneAPM 工程师编译呈现,以下为正文。
GridView 提供了一种实现分页的方法。但是,随着记录的不断扩大,我们需要在查询层面进行优化。
简介
在 SQL 查询层面实现 ASP.NET 程序分页,而不借助 GridView。
背景
无可否认,GridView 是在 ASP.NET Web 表单展示数据的强大工具,它能在结果集较大时实现分页。然而,后端会获取完整的数据,抽取出相关数据,然后通过 GridView 展示在 Web 表单中。在这种情况下,相关数据只是完全抽取数据的一小部分。这些多余的数据造成了处理能力、内存空间以及时间的极大浪费。在本文中,我们将展示如何仅从数据库抽取所需数据,从而避免这些浪费。
下图展示了从数据库中获取完整数据的方式。在渲染阶段,相关数据会被抽取出来,填充到 GridView 中。

下图展示了从数据库中抽取过滤或相关数据的方式,进而得到更小的数据集。在 Web 应用中,同样的数据集无需经过进一步抽取,就可以填充到 GridView 中。

具体实现
工具
本例借助 SQL Server 2014 与 Visual Studio 2015 实现。2012 之前的 SQL Server 版本不支持 FETCH,但是使用 ROW NUMBER 可以达到同样的效果。
首先进行后端设置:
- 创建名为 TestPagingInASPNET 的数据库,
- 创建名为 AdministrativeUnits 与 Cities 的两张表。
- 创建存储过程(Stored Procedures,简称 SP),用于从数据库获取数据。请注意,笔者创建了两个存储过程,名字分别为 SelectCitiesWithPaging 与 SelectCitiesWithPagingOldSQLVersions。由于笔者是在 SQL Server 2014 中实现该解决方案的,在第一个 SP 中,笔者使用了 OFFSET FETCH 声明。对于更早的版本,比如 SQL Server 2005 与 SQL Server 2008,则应该使用 ROW_NUMBER() 函数而非 OFFSET FETCH。因此,请创建与开发环境相适合的 SP。与传统的 SP 不同,此处创建的 SP 将包含三个参数,细节如下:
@PageNumber 为将会返回的页码数(Page Number)
@RowsPerPage 为每页的行数(Number of Rows)
@TotalResords(输出参数)为总的记录
-- CREATE DATABASE
CREATE DATABASE TestPagingInASPNET;
GO
-- CREATE FIRST TABLE
CREATE TABLE AdministrativeUnits (
AdministrativeUnitID INT PRIMARY KEY IDENTITY(1, 1),
Name VARCHAR(50)
);
GO
-- CREATE SECOND TABLE
CREATE TABLE Cities (
CityID INT PRIMARY KEY IDENTITY(1, 1),
AdministrativeUnitID INT,
Name VARCHAR(50)
);
GO
-- CREATE THE STORED PROCEDURE
CREATE PROCEDURE SelectCitiesWithPaging
@PageNumber INT,
@RowsPerPage INT,
@TotalRows INT OUTPUT
AS
BEGIN
SET NOCOUNT ON;
SELECT @TotalRows = COUNT(*)
FROM [AdministrativeUnits] [AU]
INNER JOIN [Cities] [C] ON [AU].[AdministrativeUnitID] = [C].[AdministrativeUnitID]
SELECT [AU].[Name] [Administrative Unit],
[C].[Name] [City]
FROM [AdministrativeUnits] [AU]
INNER JOIN [Cities] [C] ON [AU].[AdministrativeUnitID] = [C].[AdministrativeUnitID]
ORDER BY [AU].[Name], [C].[Name]
OFFSET ((@PageNumber - 1) * @RowsPerPage) ROWS FETCH NEXT @RowsPerPage ROWS ONLY
END
GO
-- CREATE THE STORED PROCEDURE
CREATE PROCEDURE SelectCitiesWithPagingOldSQLVersions
@PageNumber INT,
@RowsPerPage INT,
@TotalRows INT OUTPUT
AS
BEGIN
SET NOCOUNT ON;
SELECT @TotalRows = COUNT(*)
FROM [AdministrativeUnits] [AU]
INNER JOIN [Cities] [C] ON [AU].[AdministrativeUnitID] = [C].[AdministrativeUnitID]
SELECT *
FROM ( SELECT ROW_NUMBER() OVER (ORDER BY [AU].[Name], [C].[Name]) NUMBER,
[AU].[Name] [Administrative Unit],
[C].[Name] [City]
FROM [AdministrativeUnits] [AU]
INNER JOIN [Cities] [C] ON [AU].[AdministrativeUnitID] = [C].[AdministrativeUnitID]
) tbl
WHERE Number BETWEEN ((@PageNumber - 1) * @RowsPerPage + 1) AND (@PageNumber * @RowsPerPage)
END
GO
现在,讨论应用的前端部分
ASPX
- 在 Web 表单中绘制一个表格,其包含两个表格行(Table Rows)
- 在第一个表格行中推拽下放一个 GridView。此处无需启用分页,因为存储过程实现该功能。
- 在第二个表格行中,放置两个按钮来实现前页与后页之间的跳转。此外,为两个按钮创建点击事件。
- 在第三个表格行中,放置页面导航链接。
- 在下面;提供了 .aspx 文件中的代码。
<table style="width:100%;">
<tr>
<td>
<asp:GridView ID="GridView1" runat="server"></asp:GridView>
</td>
</tr>
<tr>
<td style="text-align:center;">
<asp:Button ID="btnGridViewPrevious" runat="server" OnClick="btnGridViewPrevious_Click" Text="<" />
<asp:TextBox ID="txtGridViewPageNumber" runat="server"></asp:TextBox>
<asp:Button ID="btnGridViewGoToPageNumber" runat="server" Text="Go to Page" OnClick="btnGridViewGoToPageNumber_Click" />
<asp:Button ID="btnGridViewNext" runat="server" OnClick="btnGridViewNext_Click" Text=">" />
</td>
</tr>
<tr>
<td style="text-align:center;" runat="server" id="tdPage">
</td>
</tr>
</table>
ASPX.cs
在 .aspx.cs 文件中,我们会创建两个函数。
GetAndBindData()
- 第一个函数将得到来自数据库的请求数据。请注意,我们以页码数与每页的行数为参数。
- 接收到的数据将填充在网格中。
- 在页面加载(Page Load)事件触发,且参数页面数(PageNumber)为1时,即会调用该函数。
CreatePager()
- 第二个函数将创建用于导航的链接。
private void GetAndBindData(Int32 PageNumber, Int32 RowsPerPage)
{
SqlConnection con = new SqlConnection(ConnectionString);
SqlCommand cmd = new SqlCommand();
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.CommandText = "SelectProjects";
cmd.Connection = con;
SqlParameter par1 = new SqlParameter();
par1.ParameterName = "PageNumber";
par1.DbType = System.Data.DbType.Int32;
par1.Direction = System.Data.ParameterDirection.Input;
par1.Value = PageNumber;
cmd.Parameters.Add(par1);
SqlParameter par2 = new SqlParameter();
par2.ParameterName = "RowsPerPage";
par2.DbType = System.Data.DbType.Int32;
par2.Direction = System.Data.ParameterDirection.Input;
par2.Value = RowsPerPage;
cmd.Parameters.Add(par2);
SqlParameter par3 = new SqlParameter();
par3.ParameterName = "TotalRows";
par3.DbType = System.Data.DbType.Int32;
par3.Direction = System.Data.ParameterDirection.Output;
cmd.Parameters.Add(par3);
SqlDataAdapter adp = new SqlDataAdapter();
adp.SelectCommand = cmd;
DataSet ds = new DataSet();
con.Open();
adp.Fill(ds);
Session["TotalRows"] = par3.Value.ToString();
GridView1.DataSource = ds.Tables[0];
GridView1.DataBind();
}
private void CreatePager(Int32 TotalRecords, Int32 PageNumber, Int32 RowsPerPage)
{
Int32 intIndex;
Int32 intPageNumber;
tdPage.InnerHtml = "";
intPageNumber = 1;
for (intIndex = 1; intIndex <= TotalRecords; intIndex+=10)
{
tdPage.InnerHtml += " <a href=''>" + intPageNumber.ToString() + "</a> ";
intPageNumber++;
}
if (TotalRecords > intIndex) {
tdPage.InnerHtml += " <a href=''>" + intIndex.ToString() + "</a> ";
}
}
protected void btnGridViewNext_Click(object sender, EventArgs e)
{
Int32 NewPageNumber = Convert.ToInt32(Session["PageNumber"]);
NewPageNumber++;
Session["PageNumber"] = NewPageNumber;
txtGridViewPageNumber.Text = Session["PageNumber"].ToString();
GetAndBindData(Convert.ToInt32(Session["PageNumber"]), 10);
btnGridViewPrevious.Enabled = true;
}
protected void btnGridViewGoToPageNumber_Click(object sender, EventArgs e)
{
Int32 NewPageNumber = Convert.ToInt32(txtGridViewPageNumber.Text);
Session["PageNumber"] = NewPageNumber;
txtGridViewPageNumber.Text = Session["PageNumber"].ToString();
GetAndBindData(Convert.ToInt32(Session["PageNumber"]), 10);
btnGridViewPrevious.Enabled = true;
}
要点总结
通过此方法,在用户改变页面索引时,开发者可以只获取相关数据进行展示,而非完整的数据集。这样,不仅可以从数据库中选出相关数据,在 GridView 中过滤数据所需的步骤也可以避免。从而切实提高并优化应用性能。
原文地址:http://www.codeproject.com/Articles/1078662/How-to-implement-Paging-in-ASP-NET-at-SQL-Query-Le
OneAPM 助您轻松锁定 .NET 应用性能瓶颈,通过强大的 Trace 记录逐层分析,直至锁定行级问题代码。以用户角度展示系统响应速度,以地域和浏览器维度统计用户使用情况。想阅读更多技术文章,请访问 OneAPM 官方博客。
本文转自 OneAPM 官方博客
ASP .NET 如何在 SQL 查询层面实现分页的更多相关文章
- MaxCompute如何对SQL查询结果实现分页获取
由于MaxCompute SQL本身不提供类似数据库的select * from table limit x offset y的分页查询逻辑.但是有很多用户希望在一定场景下能够使用获取类似数据库分页的 ...
- 如何在SQL Server查询语句(Select)中检索存储过程(Store Procedure)的结果集?
如何在SQL Server查询语句(Select)中检索存储过程(Store Procedure)的结果集?(2006-12-14 09:25:36) 与这个问题具有相同性质的其他描述还包括:如何 ...
- (25)ASP.NET Core EF查询(复杂查询运算符、原生SQL查询、异步查询)
1.复杂查询运算符 在生产场景中,我们经常用到LINQ运算符进行查询获取数据,现在我们就来了解下生产场景经常出现几种复杂查询运算符. 1.1联接(INNER JOIN) 借助LINQ Join运算符, ...
- 用sql查询当天,一周,一个月的数据
用sql查询当天,一周,一个月的数据 数据查询,不管在网站还是在系统,都很常见,下文是介绍最常见的以日期查询的语句 select * from ShopOrder where datediff(w ...
- 15个初学者必看的基础SQL查询语句
本文由码农网 – 小峰原创翻译,转载请看清文末的转载要求,欢迎参与我们的付费投稿计划! 本文将分享15个初学者必看的基础SQL查询语句,都很基础,但是你不一定都会,所以好好看看吧. 1.创建表和数据插 ...
- cache应用(asp.net 2.0 SQL数据缓存依赖 [SqlCacheDependency ] )
Asp.net 2.0 提供了一个新的数据缓存功能,就是利用sql server2005 的异步通知功能来实现缓存 1.首先在sqlserver2005 中创建一个test的数据库. 在SQL Ser ...
- C# EF使用SqlQuery直接操作SQL查询语句或者执行过程
Entity Framework是微软出品的高级ORM框架,大多数.NET开发者对这个ORM框架应该不会陌生.本文主要罗列在.NET(ASP.NET/WINFORM)应用程序开发中使用Entity F ...
- ASP.NET代码调用SQL Server带DateTime类型参数的存储过程抛出异常问题
ASP.NET代码调用SQL Server带DateTime类型参数的存储过程,如果DateTime类型参数的值是'0001/1/1 0:00:00'时,就会抛出异常“Message: SqlDate ...
- 04、SQL 查询当天,本月,本周的记录
SELECT * FROM 表 WHERE CONVERT(Nvarchar, dateandtime, 111) = CONVERT(Nvarchar, GETDATE(), 111) ORDE ...
随机推荐
- 10款让你心动的 HTML5 & CSS3 效果
这里集合的这组 HTML5 & CSS3 效果,有的是网站开发中常用的.实用的功能,有的是先进的 Web 技术的应用演示.不管哪一种,这些案例中的技术都值得我们去探究和学习. 1.超炫的 HT ...
- uml的关联多重度
UML中关联的多重度是指一个类的实例能够与另一个类的多少个实例相关联,这个“多少”被称为关联角色的多重度指定关联一端的多重度.也可以这样理解:在关联另一端的类的每个对象要求在本端的类必须有多 少个对象 ...
- CityEngine2012(32位)安装
今天下午把CityEngine2012装好了,既然Esri大力推CityEngine作为其三维建模软件,那就学习一下,还好没花多长时间搞定破解版,以前装Erdas,南方CASS,AutoCAD那些该死 ...
- 【风马一族_Java】java的一种IDE
IntelliJ IDEA 14 下载地址: IntelliJ IDEA 14 下载 分享几个license: (1) key:IDEA value:61156-YRN2M-5MNCN-NZ8D2-7 ...
- 《boot分区监控的小脚本》
#!/bin/bash TEST=`df | grep "boot" |awk '{print $5}' |cut -f1 -d"%"` if [ $TEST ...
- Linux系统环境变量及命令
Linux哪些我们常用,但是用的时候想不起来,这里做一个备忘录. Linux常用的变量: PATH 决定了shell将到哪些目录中寻找命令或程序 HOME 当前用户主目录 HISTSIZE 历史记录数 ...
- 京东sdk商家上架接口调用问题总结(更新中...)
前言: 最近在做商家发布产品,调用京东sdk,发现问题很多,而且还是在我同事的帮助下完成的,摸索中,菜鸟还请高手门多多提携才好,入正题 首先是引用jd的sdk啦,京东sdk中发布商品需要调用一个 36 ...
- 惠普M1005打印机无法自动进纸的问题
惠普M1005打印机无法自动进纸的问题 问题起因 其实我也不太清楚是什么起因,前一天用的好好的惠普M1005打印机,在打印时没有直接打印,会弹出一个提示对话框,同时打印机显示屏上显示“load tra ...
- SATA SAS SSD 硬盘介绍和评测
SATA SATA的全称是Serial Advanced Technology Attachment,是由Intel.IBM.Dell.APT.Maxtor和Seagate公司共同提出的硬盘接口规范. ...
- Redis源码研究--跳表
-------------6月29日-------------------- 简单看了下跳表这一数据结构,理解起来很真实,效率可以和红黑树相比.我就喜欢这样的. typedef struct zski ...