此外,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 语句:
因为未对 Orders 数据表做 WHERE 条件过滤,会如下图 1 所示,将里面的 830 笔记录全部捞出、用 DESC 反向排序后,再由 ROW_NUMBER 函数提供一个重新编号过的流水号字段「字段编号」,从 1 号自动给号至 830 号:
图 1 图中右下角的 830 个数据列,事实上是指「记录 (record);行 (row)」。简体中文和繁体中文对数据表的「行」、「列」使用上刚好相反
-------------------------------------------------
若我们改执行下列的 SQL 语句 (取自 SQL Server 在线丛书):
图 2
则可由 ROW_NUMBER 函数模拟的临时数据表中,取得我们写分页时,所需要的某个范围内的数据记录笔数。以上图 2 来说,即只撷取 DESC 反向排序后、5 至 13 号的这九笔记录。且这种 WITH 的 T-SQL 新语法,又称为「一般数据表表达式 (CTE, common_table_expression)」,也是与 ROW_NUMBER 函数搭配,撰写分页程序的精要所在,在 SQL Server 在线丛书也有相关介绍。
-------------------------------------------------
版工还在网络上的论坛 [5],看到别人提供的语法,先用 ROW_NUMBER 函数取得数据的顺序编号,再用 WHERE 条件过滤:
假设我们的 GridView 每页要显示 10 笔记录,user 目前在 GridView 的第 20 页,当他单击「下一页」或第 21 页的页码时,就去 SQL Server 2005 撷取第 201 ~ 210 笔记录,在 Stored Procedure 里即执行下列 SQL 语句。撷取结果如下图 3 所示:
图 3 GridView 每页显示 10 笔记录,若使用者按下了第 21 页的页码
-------------------------------------------------
但以 ROW_NUMBER 函数撰写分页的话,亦要考虑系统以后无法更换数据库的问题;且用 ROW_NUMBER 写好的 Stored Procedure,在其它 project 中,也无法重复使用于他牌的数据库,或旧版的 SQL Server。就如同 ADO.NET 2.0 中,有新增一些针对 SQL Server 2005 可提升 performance 的 .NET 数据处理语法,但使用前应先评估,系统日后是否有移植或维护上的问题。
-------------------------------------------------
本帖参考文件:
[1] 揚棄土法煉鋼、巧妙運用排序函數 實現SQL Server 2005自動分頁非難事:
http://oa.digitimes.com.tw/print.aspx?zNotesDocId=0000040434_B51LZD55XF2OFQG5TTEZM
[2] Sql Server 2005 ROW_NUMBER 函数实现分页:
http://www.cnblogs.com/shanyou/archive/2006/08/22/rownumber.html
[3] 當 DataGrid 遇見 100 萬筆資料:
http://blog.sina.com.tw/4907/article.php?pbgid=4907&entryid=3921
[4] 請問有關開啟頁面時,一次載入數千筆資料的效能問題:
http://www.blueshop.com.tw/board/show.asp?subcde=BRD200709141021458MV
[5] 老問題 - GridView 分頁:
http://www.purecs.net/thread/topic368_1.aspx
-------------------------------------------------
本帖相关文件:
[1] SQL Server 2005 中的 ROW_NUMBER 和 RANK:
http://blog.csdn.net/leizhipan/archive/2006/10/19/1341245.aspx
[2] SQL Script 應用實例系列(01)--將同一資料表的每三筆資料組合成一筆:
http://blog.blueshop.com.tw/jacksun/archive/2007/03/07/49860.aspx
[3] SQL Script 應用實例系列(02)--分群組排名的問題 :
http://blog.blueshop.com.tw/jacksun/archive/2007/03/08/49883.aspx
[4] 10 Tips for Writing High-Performance Web Applications:
http://msdn.microsoft.com/msdnmag/issues/05/01/ASPNETPerformance/
-------------------------------------------------
(有些 Hyperlink 是连到台湾的网站,若无法连结,麻烦请留言反应)
Feedback
1858117请问在sql Server 2000里面怎么分页呢?
还有就像你说的
“考虑系统以后无法更换数据库的问题”
“就如同 ADO.NET 2.0 中,有新增一些针对 SQL Server 2005 可提升 performance 的 .NET 数据处理语法,但使用前应先评估,系统日后是否有移植或维护上的问题。”
这个怎么解决呢?
不知道你有没有看过我写的分页控件 —— QuickPager。
这个分页控件就解决了你说的这几个问题。
他提供了多种分页算法,每一种分页算法都是“按需所取”,就是你说的“如 GridView 若每页要显示 10 笔记录,就只从数据库撷取 10 笔记录,”。
为什么要提供多种分页算法呢,就是为了能够适应多种数据库,和日后算法的升级。
比如如果您使用SQL Server2005 的话,那么可以选择 Row_Number分页算法,这个就是您说的这种算法。
而对于SQL Server 2000 、Access等数据库的话,可以选择其他的分页算法,比如临时表、Max、颠倒top等。
而在使用分页控件的时候既不用写存储过程,也不用把sql语句直接赋给分页控件,而只需要设置要显示的表名(或者视图名)、要显示的字段、排序字段等数据就可以了,分页控件内部会根据选择的分页算法,把这些属性组合成分页用的SQL语句,然后提交给数据库。
这样就弥补了您说的这几种缺点。
另外还可以根据不同的情况,选择适合的分页算法。