詳解ASP.NET在SQL Server 2005上自定義分頁
WEB開發(fā)中普遍會用頁面來顯示數(shù)據(jù)。比起整頁顯示一張報表或者一張數(shù)據(jù)表的數(shù)據(jù)給用戶,開發(fā)者經(jīng)常用到的是分頁顯示,每頁只顯示部分?jǐn)?shù)據(jù),用翻頁來控制。在ASPV.NET 1.X里,DataGrid控件使翻頁顯示變得簡單—只需要把屬性AllowPaging設(shè)置為”true”,并在PageIndexChanged事件中加少量幾行的代碼就可以實現(xiàn)!ASP.NET 2.0中的控件GridView使事件也簡單化了,只需要在GridView的智能面板里把“允許分頁”選中,不需要一行代碼就可以實現(xiàn)。
當(dāng)然了,生活中沒有任何事是容易就能做好的。你需要權(quán)衡選中一個復(fù)選框就能實現(xiàn)的分頁方案(或者用DataGrid,寫幾行代碼的實現(xiàn)方案)的性能。如果不選中分頁,DataGrid和GridView使用默認(rèn)分頁,簡單的把所有數(shù)據(jù)從頭到尾地顯示在一張網(wǎng)頁上。當(dāng)數(shù)據(jù)量小時,幾十一百或類似的數(shù)量時,效率性能不會太明顯。但是,如果你想像這樣用默認(rèn)的分頁方法顯示上千萬、以至幾十萬的書時就不可行了。
取代默認(rèn)分頁的方法就是自定義分頁,你要做的工作就是用代碼來判斷并把正確的分頁數(shù)據(jù)取出,可能會費點事,但對于應(yīng)付如此龐大的數(shù)據(jù)量來說,絕對值得。我們來看在ASP.NET 2.0中如何利用SQL Server 2005的新特性 ROW_NUMBER()來實現(xiàn)自定義分頁。(更多關(guān)于SQL SERVER的新特性說明,請看利用Microsoft SQL SERVER 2005返回列值。)
默認(rèn)分頁與自定義分頁對比
在ASP.NET 2.0里GridView(或ASP.NET 1.X里的DataGrid)提供兩種分頁模型:默認(rèn)分頁與自定義分頁。這兩種模型在性能與易用性上提供了折中的方案。SqlDataSource控件使用默認(rèn)分頁(盡管你可以在自定義分頁了使用它);ObjectDataSource默認(rèn)使用默認(rèn)分頁模型,不過有個簡單的配置可以讓它使用自定義分頁。心里要時刻記得GridView僅僅是顯示數(shù)據(jù);它才是GridView負(fù)責(zé)從數(shù)據(jù)庫中檢索數(shù)據(jù)的數(shù)據(jù)源控件。
使用默認(rèn)分頁,每次打開新頁顯并顯示時,都要從GridView的數(shù)據(jù)源控件中獲得所有數(shù)據(jù)。一旦全部的數(shù)據(jù)返回,GridView就把所有的數(shù)據(jù)全顯示在頁面上,于是用戶看到一張頁面上顯示了如此多的數(shù)據(jù)。關(guān)鍵要理解這里,無論何時當(dāng)用戶訪問***頁或翻到其他頁時,所有的數(shù)據(jù)都會被重新加載一遍。
舉個例子,比如說你在一家電子商務(wù)公司上班,你想讓用戶分頁查看你們公司所銷售的150中產(chǎn)品。并且,你想每頁只顯示10條數(shù)據(jù)。現(xiàn)在,當(dāng)一個用戶訪問網(wǎng)頁時,所有150條數(shù)據(jù)都被數(shù)據(jù)源空間返回過來。但GridView只顯示***個10條數(shù)據(jù)(產(chǎn)品1到產(chǎn)品10)。再想象一下,當(dāng)這個用戶翻看第二頁的數(shù)據(jù)時,這會引發(fā)一個回發(fā)的事件,而且在這個時候GridView又會從數(shù)據(jù)源控件中獲得所有的150條記錄,但這時只需要顯示第二個10條(產(chǎn)品11到產(chǎn)品20)。
緩存與SqlDataSource
SqlDataCourse在對屬性EnableCaching做簡單設(shè)置后允許數(shù)據(jù)集緩存數(shù)據(jù)。對于一個緩存的數(shù)據(jù)集,翻頁時不需要再訪問數(shù)據(jù)庫,從分頁開始到結(jié)束都緩存在內(nèi)存里。然而,初始化頁面時相同的問題發(fā)生了—所有的數(shù)據(jù)必須載入到緩存的數(shù)據(jù)集里去。此外,這樣的話你還必須擔(dān)憂那些過期的數(shù)據(jù)(雖然你使用了SQL cache dependencies ,但在這里已經(jīng)沒意義了)。
在我不太科學(xué)的測試下,發(fā)現(xiàn)緩存數(shù)據(jù)和自定義分頁的速度差2倍以上……盡管,你看到緩存方式接近不緩存的方式。(但它始終沒有超過自定義分頁?。?/P>
更多關(guān)于SqlDataSource的數(shù)據(jù)集緩存,請看利用SqlDataSource緩存數(shù)據(jù).
使用自定義分頁,你,開發(fā)者,需要做一些工作,但比起盲目的把GridView綁定到一個數(shù)據(jù)源控件,并且選中“允許分頁”復(fù)選框,倒不如配置一下數(shù)據(jù)源控件,使它只檢索某一頁需要顯示的部分?jǐn)?shù)據(jù)。這樣做的好處是,當(dāng)顯示***頁的數(shù)據(jù)時,你可以寫一段只檢索產(chǎn)品1到產(chǎn)品10的sql語句,而不是把所有150條記錄全取出來。不過,你的sql語句需要“聰明”的知道怎么把需要的數(shù)據(jù)從150條記錄里剪切出來。
自定義分頁的性能優(yōu)勢
我們可以從數(shù)據(jù)記錄的檢索能看出自定義分頁比默認(rèn)分頁的性能好。在我們的例子里,假設(shè)有150條產(chǎn)品數(shù)據(jù),每頁顯示10條。如果用自定義分頁,用戶挨個瀏覽這15頁的數(shù)據(jù),150條數(shù)據(jù)會分批檢索出來;如果用自定義分頁,不管哪頁150條數(shù)據(jù)都會被檢索出來,導(dǎo)致全部的數(shù)據(jù)檢索量也許是15倍的15條,可能有2250條之多!
雖然自定義分頁的性能顯而易見,但默認(rèn)分頁卻非常簡單。所以當(dāng)數(shù)據(jù)量小并且數(shù)據(jù)庫服務(wù)器的負(fù)載也不太重時,我推薦你使用默認(rèn)分頁。如果你有幾百、上千以至上萬條數(shù)據(jù)需要分頁顯示時,一定要使用自定義分頁。當(dāng)然,像分頁ASPFAQ.com的數(shù)據(jù)庫,現(xiàn)在的數(shù)據(jù)量只有200條問答,用默認(rèn)分頁足夠了。(當(dāng)然,如果你覺得數(shù)據(jù)量將來不會增長,比如可能會維持在75條左右,那你就使用默認(rèn)分頁。但如果將來數(shù)據(jù)會增長到7500條而你卻使用了默認(rèn)分頁,一定會有些客戶不高興的?。?/P>
利用SQL Server 2005高效地取回一頁數(shù)據(jù)
像早前4Guys 的一篇文章講述的那樣,利用Microsoft SQL Server 2005獲得行值中提到的, SQL Server 2005 引入了許多返回行值的關(guān)鍵詞。特別提到關(guān)鍵詞the ROW_NUMBER()可以返回一列遞增的行數(shù)。因此,我們可以使ROW_NUMBER()寫一條像下面的sql查詢語句來獲得某頁的數(shù)據(jù):
SELECT ... |
這里的@startRowIndex表示開始行的索引,@maximumRows表示每頁顯示的***條數(shù)。這條語句會返回ROW_NUMBER()從開始的索引行數(shù)到加每頁***條數(shù)的行數(shù)一部分?jǐn)?shù)據(jù)。
為了使表示跟具體,我們來看下下面這個例子。假設(shè)我們有一個表Employees有5000條數(shù)據(jù)(公司很不錯哦?。?,看下面的查詢語句:
SELECT RowNum, EmployeeID, LastName, FirstName |
返回的結(jié)果如下:
RowNum EmployeeID LastName FirstName |
注意即使EmployeeID字段中間可能有短缺或者可能不是從1開始的,ROW_NUMBER()也會從第1條記錄開始,并且穩(wěn)定增長。因此,如果你想每頁顯示10條數(shù)據(jù),我們想看第3頁時,我們知道我們需要的數(shù)據(jù)是第31條到第40條,我們可以用一個簡單的WHERE查詢得到。
配置ObjectDataSource支持自定義分頁
正如前面提到的,一方面,SqlDataSource并沒有設(shè)計成能提供自定義分頁;另一方面,ObjectDataSource被設(shè)計成了能支持這個方案。ObjectDataSource被設(shè)計成能從一個object讀取數(shù)據(jù)的數(shù)據(jù)源控件。這個object不管從哪、怎樣取出數(shù)據(jù),像從一個WEB 服務(wù)、一個數(shù)據(jù)庫、一個文件系統(tǒng),或一個XML文件……等等。ObjectDataSource并不關(guān)心,它就像在數(shù)據(jù)存儲者和數(shù)據(jù)需要者(像一個GridView控件)之間充當(dāng)了一個中介。(更多關(guān)于ObjectDataSource請看ObjectDataSource控件概述)
當(dāng)把一個WEB數(shù)據(jù)控件綁定到一個ObjectDataSource并設(shè)置為“允許分頁”時,如果你沒有具體設(shè)置ObjectDataSource來支持自定義分頁,會使用默認(rèn)方案來分頁。要使ObjectDataSource支持自定義分頁,你需要使一個object來提供以下這些方法:
1.一個包含兩個整型參數(shù)的方法。***個整數(shù)表示檢索數(shù)據(jù)開始的索引位置(起點),而第二個整數(shù)表示每頁顯示的***條數(shù)。這個方法在調(diào)用時會返回我們所請求的數(shù)據(jù),也就是從檢索開始位置到往后每頁顯示***條數(shù)的準(zhǔn)確記錄,而不是把所有數(shù)據(jù)記錄都取出來。
2.一個返回全部分頁的數(shù)據(jù)記錄數(shù)量(整數(shù)類型)的方法。(這是WEB數(shù)據(jù)控件在在初始化時需要的,這樣就可以顯示出要分出的總頁數(shù)或者是是否還需要有下一頁。)
如果你要用到基礎(chǔ)的object類型,配置ObjectDataSource來實現(xiàn)分頁是很簡單的,來看一下下面的這些ObjectDataSource的屬性:
把EnablePaging設(shè)置為True
把SelectMethod設(shè)置為提供開始頁和開始***行數(shù)的參數(shù)以檢索數(shù)據(jù)的方法
把StartRowIndexParameterName設(shè)置為你在SelectMethod中用到的表示檢索開始位置的變量;如果你不設(shè)置這個,它默認(rèn)為取startRowIndex的值
把MaximumRowsParameterName設(shè)置為你在SelectMethod用到的每頁***記錄數(shù),如果你不設(shè)置這個,它默認(rèn)取maximumROws的值
把SelectCountMethod設(shè)置為返回分頁的所有記錄的總數(shù)的方法
就這么簡單。如果你像這樣配置了,那么ObjectDataSource就使用自定義分頁了。當(dāng)然,難點是創(chuàng)建能正確的取出數(shù)據(jù)的基礎(chǔ)對象。但你一旦有了這個基礎(chǔ)對象,要實現(xiàn)自定義分頁也就配置一下ObjectDataSource的幾個屬性而已。
創(chuàng)建一個支持自定義分頁的對象
為了使一個ObjectDataSource綁定到一個GridView,我們需要一個能靈活取得需要數(shù)據(jù)的底層對象,這個對象還能夠返回需要分頁的記錄數(shù)。像約瑟夫校長的文章中寫的那樣, 《在Visual Studio 2005和中ASP.NET 2.0使用強(qiáng)對象類型》和布萊恩·諾伊斯的文章 《利用Visual studio 2005 數(shù)據(jù)集設(shè)計器做數(shù)據(jù)訪問層》中講述的那樣,在Visual studio 2005中創(chuàng)建能綁定到ObjectDataSource上的對象集是一件很容易的事情。首先要定義取出數(shù)據(jù)集的存儲過程(或sql查詢語句),以便讓那些強(qiáng)類型的數(shù)據(jù)集返回數(shù)據(jù)。
接下來,在本文的***提供的,有一個包含5000條職員的范例數(shù)據(jù)庫(大批量增加記錄是很容易的)。這個數(shù)據(jù)庫包含了2個自定義分頁示例中要用到的3個存儲過程:
GetEmployeesSubset(@startRowIndex int, @maximumRows int) – 返回按EmployeeID排序后從@startRowIndex開始的最多@maximumRows條記錄。
GetEmployeesRowCount – 返回Employees表里的記錄總數(shù)。
GetEmployeesSubsetSorted(@sortExpression nvarchar(50), @startRowIndex int, @maximumRows int) – 這個存儲過程返回一頁經(jīng)過指定條件排序的數(shù)據(jù)。也就是說能返回比如支持薪水排序后的記錄。(GetEmployeesSubset只能返回按EmployeeID排序的記錄。)這個擴(kuò)展當(dāng)你需要使用自定義分頁中有自定義排序時會用到。
在這篇文章里我們不計劃講述關(guān)于自定義分頁中的自定義排序,盡管在這篇文章的下載中也提供了這樣的例子;可以通過《自定義分頁中的數(shù)據(jù)排序》看到如何建立自定義分頁并支持雙向排序……
創(chuàng)建完這些存儲過程以后,我在我的項目里加入一個強(qiáng)類型的數(shù)據(jù)集文件(Employees.xsd)。然后在里面加入三個與三個存儲過程相對應(yīng)的方法。***我加入了一個返回EmployeesTableAdapter對象的方法GetEmployeesSubset(startRowIndex,maximumRows)和一個可以設(shè)置到ObjectDataSource屬性上的方法GetEmployeesRowCount()。(關(guān)于創(chuàng)建強(qiáng)類型數(shù)據(jù)集的詳細(xì)步驟去看《在Visual Studio 2005和 ASP.NET中使用強(qiáng)類型數(shù)據(jù)集》和思考特·格思里的博客文章《在VS2005和ASP.NET 2.0中用強(qiáng)類型數(shù)據(jù)集創(chuàng)建數(shù)據(jù)訪問層》。)
默認(rèn)分頁與自定義分頁性能比較
在本文***有關(guān)于默認(rèn)分頁與自定義分頁的性能比較的數(shù)據(jù)庫(包含一張5000條數(shù)據(jù)的表),我用SQL和ASP.NET找出性能的差距。(這些測試在我電腦上做是很不嚴(yán)謹(jǐn)?shù)模驗槲业碾娔X還同時運(yùn)行著其他的程序,雖然結(jié)果不能被稱做證明,但我想自定義分頁的性能在比較中肯定是有優(yōu)勢的。)
SQL查詢結(jié)果
默認(rèn)分頁
(從Employees檢索所有記錄)
持續(xù)時間 (秒) 讀取數(shù)量
1.455 383
1.405 383
1.434 383
1.394 383
1.365 383
平均: 1.411 平均: 383
自定義分頁
(從Employees檢索一頁記錄)
持續(xù)時間 (秒) 讀取數(shù)量
0.003 29
0.000 29
0.000 29
0.003 29
0.003 29
平均: 0.002 平均: 29
ASP.NET測試結(jié)果
默認(rèn)分頁
(從Employees查詢所有記錄)
頁面載入時間(秒)
2.34136852588807
2.35772228034569
2.43368277253115
2.43237562315881
2.33167064529151
平均: 2.379363969
自定義分頁
(從Employees查詢一頁記錄)
頁面載入時間(秒)
0.0259611207569677
0.0280046765720224
0.0359054013848129
0.0295534767686955
0.0300096800012292
平均: 0.029886871
SqlDataSource緩存
(查詢所有記錄,但緩存它們)
頁面載入時間(秒)
2.39666633608461
0.0431529705591074
0.0443528437273452
0.0442313199023898
0.0491523364002967
平均: 0.515511161
正如你看到的,自定義分頁要比默認(rèn)分頁快2倍以上。在相同的數(shù)據(jù)量下,GetEmployeesSubset(@startRowIndex int,@maximumROws int)要比簡單的從Employees表中SELECT查詢出所有記錄要快470倍。在相同的ASP.NET環(huán)境下,自定義分頁要比默認(rèn)分頁快120倍。.高負(fù)荷的工作量使兩者接近,也就是說建立數(shù)據(jù)庫連接和分配任務(wù)都會降低性能。不管怎樣,在性能方面兩個數(shù)量級相差太大了。并且這種差距會隨著數(shù)據(jù)量的增加或服務(wù)器的性能不同使載入更加明顯。
當(dāng)緩存是空的時,SqlDataSource方案消耗時間很長,因為它必須到數(shù)據(jù)庫去取得所有數(shù)據(jù),緩存在服務(wù)器。而服務(wù)器有空閑資源時才重新載入(如果只有少量的資源,緩存的數(shù)據(jù)集就會被清除出內(nèi)存)并且不再載入。在數(shù)據(jù)被緩存后,雖然在性能上很接近自定義分頁,但0.516秒的平均時間會隨著越來越多的緩存數(shù)據(jù)而接近0.05秒。
總結(jié)
ASP.NET 1.x中的DataGrid和2.0中的GridView有兩種分頁方案:默認(rèn)分頁與自定義分頁。默認(rèn)分頁很容易做到,但每次訪問每個頁面都會訪問數(shù)據(jù)庫并取出所有數(shù)據(jù)。而自定義分頁非常高效,靈活地取出該取出的那些數(shù)據(jù)。SQL SERVER 2005由于它的新特性ROW_NUMBER()能取出行數(shù)的能力,使取出某段數(shù)據(jù)簡單了。
如果你做的WEB程序現(xiàn)在或?qū)碛锌赡茏層脩舴摬榭待嫶蟮臄?shù)據(jù),那么你用自定義分頁是很合適的。
【編輯推薦】