| Web應(yīng)用程序是顯示數(shù)據(jù)庫中數(shù)據(jù)的一個非常好的方法,通過它,你可以把業(yè)務(wù)復(fù)雜,并有訪問和安全規(guī)則的數(shù)據(jù)庫數(shù)據(jù)以一種簡單、直觀的方式向用戶提供查詢和更新的功能。用戶判斷數(shù)據(jù)庫應(yīng)用程序一個很常用的標準就是 處理數(shù)據(jù)的快慢。許多Web頁面都向用戶提供了多種可搜索的列表顯示來有效地定位記錄的位置,一個比較簡單而且常用的例子就是在線圖書查詢系統(tǒng),它允許用戶可以按作者,按書名或者按主題來檢索圖書信息。 ASP.NET提供了一個DataGrid控件可以比以前的ASP方便地創(chuàng)建創(chuàng)建數(shù)據(jù)列表,DataGrid控件除了內(nèi)建的數(shù)據(jù)表現(xiàn)和方法之外,還允許用戶自己定義表現(xiàn)形式。分頁技術(shù)為用戶可管理的數(shù)據(jù)查找提供方便。DataGrid內(nèi)建的分頁技術(shù)很容易實現(xiàn),但數(shù)據(jù)量很大時,它的方便性是以犧牲性能為代價的。下面,我們就看看如何通過自定義的分頁方法來實現(xiàn)快速處理大量數(shù)據(jù)的結(jié)果集的辦法。我們這里討論的方法比DataGrid的默認分頁方法是快速和更加有效的,這是因為每次請求不需要把全部的數(shù)據(jù)結(jié)果發(fā)送到Web服務(wù)器。相反,它只需要發(fā)送每個頁面需要的那些數(shù)據(jù)集。例如:如果一個用戶只要求100個頁面中每頁顯示25條記錄的第4頁的結(jié)果集,服務(wù)器只需要發(fā)送第75-100行的數(shù)據(jù)即可,而不是1-1000行的完全數(shù)據(jù)。默認的傳送方式如圖1所示: 從圖中可以看出,DataGrid的內(nèi)建分頁方法是效率不高的,每次請求都必須把整個查詢結(jié)果發(fā)送給Web服務(wù)器,Web服務(wù)器再把數(shù)據(jù)分成相應(yīng)的頁面。利用DataGrid的內(nèi)建的分頁方法盡管是很簡單的,但是,由于Web應(yīng)用的無序性特征,一個用戶每次從一個頁面轉(zhuǎn)向另外一個頁面時,DataGrid對象都被銷毀并重新創(chuàng)建,這就意味著數(shù)據(jù)庫服務(wù)器每次都必須發(fā)送全部的結(jié)果集。 自定義的分頁方法只返回所要檢索的那些結(jié)果集,如下圖2所示: 從上面的圖中可以看到,數(shù)據(jù)庫每次只需要返回所要顯示的數(shù)據(jù)記錄。首先,我們在數(shù)據(jù)庫中建立一個存儲過程,并有兩個輸入?yún)?shù),分別是要返回數(shù)據(jù)的第一條記錄數(shù)和最后一條記錄數(shù),在SQL Server7.0以上的版本中,都有一個top關(guān)鍵字限制返回到結(jié)果集中的前多少條記錄數(shù),然而不幸的是,沒有一個方法可以返回中間一部分的數(shù)據(jù),例如第75條記錄到100條記錄的數(shù)據(jù)。Oracle中有一個rownum()的擴展函數(shù)可以返回中間的記錄,比如:"select * form Authors where Author_Last_Name = 'Anderson' and rownum() >=75 and rownum() <= 100"。然而,由于Oracle是在排序之前指定rownum的值,因此,這樣的查詢"select * from Authors where rownum <= 25 order by Author_Last_Name"將得不到我們期望的結(jié)果。我們下面所講的方法是針對SQL Server的,但這里的概念對適合Oracle開發(fā)人員也是適用的。 要創(chuàng)建一個返回指定條記錄結(jié)果的存儲過程,首先必須指定返回結(jié)果集的條記錄數(shù),可以用臨時表,也可以用table變量(SQL Server 2000),兩個在性能上沒有太大的差別,但是,table變量是存儲在內(nèi)存中的,如果你的服務(wù)器內(nèi)存不多的話,可以考慮用臨時表,臨時表使用硬盤存儲結(jié)果,臨時表需要手工釋放對象,而table變量在存儲過程結(jié)束后自動釋放。下面就是我們要創(chuàng)建的存儲過程: create proc GetAuthors @Author_Last_Name as varchar(100) = null, @StartRow as int = null, @StopRow as int = null AS ---- 建立有標識符列的table變量 declare @t_table table ( [rownum] [int] IDENTITY (1, 1) Primary key NOT NULL , [Author_Last_Name] [varchar] (40) , [Author_First_Name] [varchar] (20) , [phone] [char] (12) , [address] [varchar] (40) , [city] [varchar] (20) , [state] [char] (2) , [zip] [char] (5) ) ---- 在返回指定的@StopRow行數(shù)之后停止處理查詢 Set RowCount @StopRow ---- 插入到table變量中 insert @t_table ( [Author_Last_Name],[Author_First_Name],[phone],[address],[city],[state],[zip] ) SELECT [Author_Last_Name],[Author_First_Name],[phone],[address],[city],[state],[zip] FROM authors WHERE Author_Last_Name like '%' + @Author_Last_Name + '%' ORDER BY Author_Last_Name ---- 返回到正確的結(jié)果 SELECT * FROM @t_table WHERE rownum >= @StartRow ORDER BY rownum GO 參數(shù)@StartRow和@StopRow接收整數(shù)值,代表要返回的開始記錄和結(jié)束記錄,如果要在一個25條記錄的頁面中返回第4頁,我們就可以設(shè)置@StartRow為76,@StopRow為100。我們在table變量@t_table中定義了一個叫rownum的整數(shù)類型的列,并指定為標識符列,這個列在我們這里介紹的分頁技術(shù)中是很重要的,當我們插入數(shù)據(jù)時,這個列自動增加,它將在插入數(shù)據(jù)時起排序作用。SET ROWCOUNT語句對優(yōu)化性能很關(guān)鍵,它告訴SQL Server進行限制要插入的數(shù)據(jù),如果我們要76-100條記錄之間的數(shù)據(jù),那么就可以不必插入大于100條記錄的數(shù)據(jù)。最后的SQL語句從@t_table的table變量選擇rownum大于或者等于@StartRow的那些數(shù)據(jù)集,然后把它們返回到Web服務(wù)器,由Web服務(wù)器綁定到DataGrid對象。值得注意的是:如果要得到76到100條記錄的數(shù)據(jù),我們必須往table變量中插入100條記錄的數(shù)據(jù),這意味著:如果瀏覽者請求的頁數(shù)越來越大,頁面性能也會有所下降的。例如:要顯示第100頁的數(shù)據(jù)(從第2451條記錄到第2500條記錄),我們必須先向table變量或者臨時表填充2500條記錄,因此,性能依賴于你計算機的硬件和你要返回的記錄數(shù),有測試表明,在SQL Server 2000中使用這樣的存儲過程平均在200-250毫秒內(nèi)返回第100頁,而返回第一頁只需要4毫秒。即使返回第500頁的數(shù)據(jù)(從第12451到12500條記錄)也可以在650到750毫秒內(nèi)完成。應(yīng)該說這種情況是很少見到的。 但為了減輕數(shù)據(jù)庫和網(wǎng)絡(luò)傳輸?shù)膲毫?#xff0c;設(shè)計合理的查詢結(jié)果頁數(shù)是很見效的。 現(xiàn)在,我們寫好了一個存儲過程來做分頁的工作,而不是用Web服務(wù)器來做這個事情,我們接下來要做的就是為DataGrid對象編寫代碼來使用我們的分頁技巧。DataGrid的AllowPaging、AllowCustomPaging、PageStyle屬性有助于避免我們編寫自己的代碼來跟蹤記錄瀏覽者目前在哪一個頁面訪問和都請求過哪些頁面。我們應(yīng)當設(shè)定AllowCustomPaging為True,否則,在你使用DataReader或者SQLDataReader綁定到DataGrid對象會遇到麻煩。在任何可能的情況下,應(yīng)當盡量使用SQLDataReader而不要使用DataSet來裝載DataGrid對象。據(jù)性能測試表明:在構(gòu)建列表顯示數(shù)據(jù)時,使用SQLDataReader比使用DataSet要快兩倍以上。不要設(shè)定AllowPaging和PageStyle的值,這是因為,如果使用這兩個屬性,你必須在viewstate中維護DataGrid,但為了追求性能最佳化,我們必須設(shè)定DataGrid的EnableViewState屬性為false,盡管這樣我們自己必須編寫一點代碼來實現(xiàn)我們的分頁,但是性能會有所提高的,因為在每次與Web服務(wù)器打交道時不必再在viewstate中存儲內(nèi)容了。 一旦我們關(guān)閉了DataGrid自己在viewstate中保存的能力,我們就必須自己編寫代碼來實現(xiàn)用戶從一頁導(dǎo)航到另一頁。DataGrid如果自己不在viewstate中進行保存,那么它也不再跟蹤記錄“前一頁”和“下一頁”是哪些頁面了。我們自己添加導(dǎo)航按鈕來幫助瀏覽者進行導(dǎo)航。最簡單的辦法是在頁面上增加兩個按鈕:“上一個”和“下一頁”。要進入到下一頁,我們在“下一頁”按鈕上增加click事件,通過我們的自定義分頁存儲過程請求相應(yīng)的記錄。例如:如果第一頁由第1條到第25條記錄組成,那么要導(dǎo)航到第二頁,我們就向存儲過程的@StartRow傳遞參數(shù)26,向@StopRow傳遞參數(shù)50即可,要返回到第一頁,@StartRow和@StopRow分別為1和25。 下面是使用VB.NET編寫的“下一頁”事件的例子: Private Sub ButtonNext_Click (ByVal sender As Object, _ ByVal e As System.EventArgs) Handles ButtonNext.Click viewstate("StartRow") = viewstate("StartRow") + dgrid.PageSize viewstate("StopRow") = viewstate("StartRow") + dgrid.PageSize '運行存儲過程,返回SQLDataReader dgrid.DataSource = RunSprocReturnDR (textAu_lname.Text, _ textAu_fname.Text, viewstate("StartRow"),viewstate("StopRow")) dgrid.DataBind() End Sub 從上面的例子可以看出,我們在viewstate中保存的只是@StartRow和@StopRow的信息,這比在viewstate中保存整個DataGrid對象高效的多。“下一頁”和“上一頁”按鈕提供的只是簡單的導(dǎo)航,要實現(xiàn)更詳細的導(dǎo)航信息,比如:共多少頁、自定義頁面記錄數(shù)等,也是可以的,但要記住不要使用DataGrid內(nèi)建的PagingStyle屬性。根據(jù)測試表明,不保存DataGrid會提高性能到54%。 列表顯示信息的性能對瀏覽者的訪問是很重要的,設(shè)計不好的列表顯示會大大降低應(yīng)用程序的性能,不管它的后端數(shù)據(jù)庫是多么快速。使用自定義分頁技術(shù),我們可以避免DataGrid默認分頁機制帶來的缺陷,如果要實現(xiàn)可搜索的列表顯示,讓你的用戶感到你的應(yīng)用程序快速和可擴展,還要編寫更多的代碼,相信各位會編寫出更加優(yōu)秀的程序的。 |