ASP.NET 数据分页第一篇—探讨分页原理及 SQL Server 2005 的 ROW_NUMBER 函数

AI权益加码!Claude Code、Cursor等20+工具免费用! 购周边限时加赠Coding Plan Lite,畅享主流AI工具!学习进阶更高效! 阅读详情

ASP.NET 数据分页第一篇—探讨分页原理及 SQL Server 2005 的 ROW_NUMBER 函数

    ASP.NET 的 GridView 控件、SqlDataSource 和 ObjectDataSource 控件,其内建的「数据分页 (pager)」功能,默认做法,是当 user 每次单击 GridView 的页码换页时,都重新去数据库里,把数据表的「所有」数据,「全部」重新 SELECT 一次,并「全部」传送至 AP server (IIS),并将大量数据「全部」存储至 memory 里的 DataSet / DataTable,再将「所有」的数据,在 memory 里做分页处理后,再「全部」传送至 user 的浏览器中,最后才呈现在 GridView 控件中;因此当数据表累积了很多笔记录后,例如一百万笔 record 时,因为 user 每次换页或按 GridView 的字段 title 排序时,背后运作,都是把一百万笔 record「全部」重新 SELECT 一次,因此就会在:数据库 → IIS、DataSet → GridView 时,出现严重的 performance 问题,且会浪费大量网络频宽,浪费大量 DB server、AP server 的 CPU、memory 硬件资源;当多人同时上线时问题更严重,甚至会造成 server 当机。

架构图如下:
使用者 (Browser + GridView)AP server (IIS) + DataSet / DataTableDB server (Stored Procedure 或 RowNumber 函数)

以版工自己的经验,当数据表的数据量累积到 5000 笔以上时,即使只有一个 user 在线,每次单击 GridView 的页码换页时,都要等 2 秒以上;因为每次换页时,都是 5000 笔记录全部重捞。而且此种情况,在系统开发时期、数据量还很少时感觉不出来;都是在系统上线、数据量开始大量增加时,才会听到客户抱怨性能不佳。也因为 ASP.NET 这种「分页」的默认行为,造成许多人误以为 ASP.NET、SQL Server 性能不佳,不适合开发大型系统。

对于此种 ASP.NET 分页功能,程序员必须再额外处理,启用 ObjectDataSource 控件默认未启用的「EnablePaging="true"」,并自己写 Stored Procedure 去做分页处理,亦即每次 user 换页时,都只撷取真正需要的记录数量,而非每次都一百万笔记录全部撷取。

在网络上,有些人是用 .NET 的自订 Class + List 动态 array,取代数据库的 Stored Procedure 去处理分页。但参考本帖上方的架构图,此种做法,仍是先从数据库撷取了一百万笔数据、浪费了 DB server 大量的硬件资源,再透过网络把一百万笔数据全部传送至 IIS,此时才用 .NET 自订 Class 去处理分页和过滤,最后才把 user 真正需要的数据 (可能只有 10 笔记录),送至前端的 Browser,因此这种做法显然并非最佳解法。

还有另一种做法,是用 DataAdapter 的 Fill 方法,去做数据过滤的动作,如下方代码所示。但此种做法的问题同上,已先从数据库撷取了一百万笔记录,才从 AP server 去做数据过滤、分页处理的动作,仍非最佳解法。

int currentIndex = 0;
int pageSize = 5;

string orderSQL = "SELECT * FROM Orders ORDER BY OrderID";

// Assumes that connection is a valid SqlConnection object.
SqlDataAdapter adapter = new SqlDataAdapter(orderSQL, connection);

DataSet dataSet 
= new DataSet();
adapter.Fill(dataSet, currentIndex, pageSize, 
"Orders");


此外,SqlDataSource、ObjectDataSource 控件,都有一个 FilterExpression 属性,亦可做数据筛选的处理,但问题同上,仍然是在 AP server 的 memory 中,才去对已撷取的一百万笔记录,做数据过滤的动作。因此釜底抽薪的最佳做法,应该是从数据库端,即先过滤数据,例如 GridView 若每页要显示 10 笔记录,就只从数据库撷取 10 笔记录,以 performance 来说,这样才是最佳解法。否则以 Web-based 的 ASP.NET 系统来说,因为会有大量 user 同时上线,以一般 AP Server、DB server 的硬件资源,绝对承受不了一百万笔数据不断大量重复撷取的 loading,AP server 的 memory 也会很快就耗光造成当机。


在旧版的 SQL Server 2000 和其它厂牌的数据库,大多未内建「排序或自动给号」的函数;程序员在撰写分页的 Stored Procedure 时,可能还得用 IDENTITY 自动增号 (流水号) 的功能,先建立一个存放「临时行号 (自动增号、流水号)」的「临时数据表 (Temporary Table)」;但此种大量建立「临时数据表」的做法,会影响 DB server 的 performance,并非「分页」处理的最佳解法。而在 SQL Server 2005 中,已新增了一个专门用来「排序和自动给号」的 ROW_NUMBER 函数,可对已从数据库撷取的数据,再赋予一个「自动编号」的「字段;列 (column)」,且听说性能颇优,也更有利于 ASP.NET 分页的处理。

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

我们若在 SQL Server 2005 的 Northwind 数据库中,执行下列 SQL 语句:
 

SELECT OrderID, CustomerID, OrderDate, ROW_NUMBER() OVER(ORDER BY OrderID DESCAS 字段编号
FROM Orders 
ORDER BY 字段编号


因为未对 Orders 数据表做 WHERE 条件过滤,会如下图 1 所示,将里面的 830 笔记录全部捞出、用 DESC 反向排序后,再由 ROW_NUMBER 函数提供一个重新编号过的流水号字段「字段编号」,从 1 号自动给号至 830 号:

figure1
图 1 图中右下角的 830 个数据列,事实上是指「记录 (record);行 (row)」。简体中文和繁体中文对数据表的「行」、「列」使用上刚好相反

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

若我们改执行下列的 SQL 语句 (取自 SQL Server 在线丛书):
 

WITH 暂存表 AS
(
SELECT OrderID, CustomerID, OrderDate, ROW_NUMBER() OVER(ORDER BY OrderID DESCAS 字段编号
FROM Orders) 
SELECT * FROM 暂存表 WHERE 字段编号 BETWEEN 5 AND 13


figure2
图 2

则可由 ROW_NUMBER 函数模拟的临时数据表中,取得我们写分页时,所需要的某个范围内的数据记录笔数。以上图 2 来说,即只撷取 DESC 反向排序后、5 至 13 号的这九笔记录。且这种 WITH 的 T-SQL 新语法,又称为「一般数据表表达式 (CTE, common_table_expression)」,也是与 ROW_NUMBER 函数搭配,撰写分页程序的精要所在,在 SQL Server 在线丛书也有相关介绍。

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

版工还在网络上的论坛 [5],看到别人提供的语法,先用 ROW_NUMBER 函数取得数据的顺序编号,再用 WHERE 条件过滤:
 

SELECT TOP(分页大小) * 
FROM 
(
SELECT ROW_NUMBER() OVER (ORDER BY 排序条件) AS RowNo FROM 数据表
AS T
WHERE RowNo > ((目前页数 - 1* 分页大小) 


假设我们的 GridView 每页要显示 10 笔记录,user 目前在 GridView 的第 20 页,当他单击「下一页」或第 21 页的页码时,就去 SQL Server 2005 撷取第 201 ~ 210 笔记录,在 Stored Procedure 里即执行下列 SQL 语句。撷取结果如下图 3 所示:
 

SELECT TOP(10* 
FROM 
(
SELECT OrderID, CustomerID, OrderDate, ROW_NUMBER() OVER (ORDER BY OrderID DESCAS 字段编号 FROM Orders
AS 暂存表 
WHERE 字段编号 > ((21 - 1* 10


figure3
图 3 GridView 每页显示 10 笔记录,若使用者按下了第 21 页的页码

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

但以 ROW_NUMBER 函数撰写分页的话,亦要考虑系统以后无法更换数据库的问题;且用 ROW_NUMBER 写好的 Stored Procedure,在其它 project 中,也无法重复使用于他牌的数据库,或旧版的 SQL Server。就如同 ADO.NET 2.0 中,有新增一些针对 SQL Server 2005 可提升 performance 的 .NET 数据处理语法,但使用前应先评估,系统日后是否有移植或维护上的问题。

asp.net/sql2005 多功能分页(存储过程) 立即下载

相关推荐

ASP.NET 数据分页第一篇 - 探讨分页原理SQL Server 2005ROW_NUMBER 函数

(本帖在版工的旧 Blog 中,发表日期为 2007/10/03) ASP.NET 的 GridView 控件、SqlDataSource 和 ObjectDataSource 控件,其内建的「数据分页 (pager)」功能,默认做法,是当 user 每次单击 GridView 的页码换页时,都重新去数据库里,把数据表的「所有」数据,「全部」重新 SELECT 一次,并「全部」传送至 AP ...

weixin_33997389的博客 206

ASP.NET 2.0在SQL Server 2005上自定义分页

这篇文章讲述了如何利用SQL Server 2005的新特性来简单高效的实现分页。对于那些暂时还没用到SQL Server2005的人们,请看在大规模数据中的高效分页方法。如果需要,这篇文章会补上这里讲到的内容。   出处:http://aspnet.4guysfromrolla.com/demos/printPage.aspx?path=/articles/03150...

weixin_33895695的博客 179

SQL 高效分页(百万条数据ROW_NUMBER() OVER (ORDER BY id) | 分页

参考资料:SQL分页语句 asp.net分页之AJAX 分页 第一种方法:效率最高[sql] view plain copy print?SELECT TOP 页大小 *   FROM       (          SELECT ROW_NUMBER() OVER (ORDER BY id) AS RowNumber,* FROM table1      )   as A    WHERE R

1945

【转】ASP.NET 数据分页第一篇 - 探讨分页原理SQL Server 2005ROW_NUMBER 函数

ASP.NET 的 GridView 控件、SqlDataSource 和 ObjectDataSource 控件,其内建的「数据分页 (pager)」功能,默认做法,是当 user 每次单击 GridView 的页码换页时,都重新去数据库里,把数据表的「所有」数据,「全部」重新 SELECT 一次,并「全部」传送至 AP server (IIS),并将大量数据「全部」存储至 memo...

weixin_30835923的博客 169

SQL Server 2005: 利用新的ranking函数实现高效的数据分页操作

最近MSDN Magazine上的一篇文章10 Tips for Writing High-Performance Web Applications提到了有效的数据分页技术对提高ASP .NET程序性能的重要性;并给出了一个实现数据分页的stored procedure的例子,抄录如下: CREATE PROCEDURE northwind_OrdersPaged(    @Pag

李展的专栏 1417

Sql Server 2005 ROW_NUMBER 函数实现分页

今天朋友问到了sql server数据库分页,以前一直用oracle,还真不知道这个sql server怎么弄,google了一下,总的来说没有oracle分页来的那么直观,那么方便。不过倒是看到下边一篇文章,感觉挺清爽的,希望对需要的人有帮助,我并没有试,以后用的时候再说,先备下:  过去用SQL Server 2000分页的,大多都用到了临时表。SQL Server 2005 ROW

shishi001的专栏 847

[ASP.NET2.0]Repeater C# 分页用法的效率及简单优化(不使用存储过程)

 文中http://www.cnblogs.com/xiediy/archive/2008/09/09/1287567.html讲述了Repeater在C Sharp 中的分页方式,我自己在三层架构(大致结构如下)中测试了一下。三层架构:1、DAL:封装了SELECT * FROM table事务(1)。2、BLL:用GetAll()方法封装了对DAL的事务(1)的访问,并返回一个Li

dengxingbo的专栏 1127

ASP.NET 2.0数据教程之二十五:: 大数据量时提高分页的效率

导言 如我们在之前的教程里讨论的那样,分页可以通过两种方法来实现: 默认分页– 你仅仅只用选中data Web control的 智能标签的Enable Paging ; 然而,当你浏览页面的时候,虽然你看到的只是一小部分数据,ObjectDataSource 还是会每次都读取所有数据自定义分页– 通过只从数据库读取用户需要浏览的那部分数据,提高了性能.显然这种方法需要你做更多的工...

aoluan2964的博客 187

Using ROW_NUMBER() to paginate your data with SQL Server 2005 and ASP.NET

http://www.codeproject.com/cs/database/row_number.aspUsing ROW_NUMBER() to paginate your data with SQL Server 2005 and ASP.NETBy Jason Witty With the release of SQL Server 2005, Microsoft introduces t

净心编程 950

SQL2005+asp.net 自定义“真分页

这方法MS SQL2000不能用,因为用到row_number(),这是MSSQL2005中提供的,在2000中没有,这个函数可以获取数据结果集行的索引。 【在SQLSERVER中处理分页,必须使用存储过程或自定义函数】 --存储过程方式 CREATE PROC sp_Demo_GetPaging @startIndex INT , @endIndex I...

238
上一篇: VSS实现版本控制管理的一些使用方法
下一篇: [ASP.NET 控件实作] 整合 jQuery ContextMenu plugin 的右键选单控件
thinker28754
博客等级 码龄25年 81粉丝 303原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值