亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

asp.net中大結果集的分頁[翻譯]

2019-11-18 16:27:41
字體:
來源:轉載
供稿:網友

介紹
在Web應用程序中,對一個大數據庫結果集進行分頁已經是一個家喻戶曉的問題了。簡單的說,你不希望所有的查詢數據顯示在一個單獨的頁面中,所以帶有分頁的顯示才是更合適的。雖然在傳統的asp里這并不是一個簡單的任務,但在asp.net中,DataGrid控件把這一過程簡化為只有幾行代碼。因此,在 asp.net中,分頁很簡單,但是默認的DataGrid分頁事件會從數據庫中把所有的記錄全部讀出來放到asp.net web應用程序中。當你的數據在一百萬以上的時候,這將引起嚴重的性能問題(如果你不相信,你可以在你的應用程序中執行一個查詢,然后在任務管理器中查看 aspnet_wp.exe的內存消耗情況)這也就是為什么需要自定義分頁行為,這樣可以保證僅獲得當前頁需要的數據記錄。

在網上有很多關于這個問題的文章和帖子,還有一些成熟的解決方案。我寫這篇文章的目的不是向你展示一個可以解決一切問題的存儲過程,而是出于優化已有方法,同時為你提供一個可供測試的應用程序,這樣你就可以根據自己的需要進行開發。下文是一個很好的開始,它包含了很多不同的方法,并且給出了一些性能測試結果

《如何通過Recordset進行分頁?》

但是我對上文的大部分內容不是很滿意。第一,半數的方法是用了傳統的ADO,很明顯它們是為“古老”的asp而寫的。剩下的一些方法就是SQL Server存儲過程,并且其中的一些由于相應時間過慢而無法使用,正如你在文章最后所看到的性能結果一樣,但是還是有一些引起了我的注意。

通用化
我決定對其中的三個方法進行仔細的分析,它們是臨時表(TempTable),動態SQL(DynamicSQL)和行計數 (Rowcount)。在下文中,我更愿意把第二個方法稱為(升序-降序)Asc-Desc方法。我不認為動態SQL是一個好名字,因為你也可以把動態 SQL邏輯應用于另一個方法中。所有這些存儲過程的通病在于,你不得不估計哪些列是你即將要排序的,而不僅僅是估計主鍵列(PK Columns)而已,這可能導致一系列的問題——對于每個查詢來說,你需要通過分頁顯示,也就是說對于每不同的排序列你必須有許多不同的分頁查詢,這意味著你要么給每個排序列做不同的存儲過程(無論使用哪種分頁方法),也么你必須借助動態SQL的幫助把這個功能放在一個存儲過程中。這兩個方法對于性能有微小的影響,但是它增加了可維護性,特別是當你需要使用這個方法顯示不同的查詢。因此,在本文中我會嘗試使用動態SQL對所有的存儲過程進行歸納,但是由于一些原因,我們只能對實現部分的通用性,因此你還是得為復雜查詢寫獨立的存儲過程。

允許包括主鍵列在內的所有排序字段的第二個問題在于,如果那些列沒有作適當的索引,那么這些方法一個也幫不上忙。在所有這些方法中,對于一個分頁源必須先做排序,對于大數據表來說,使用非索引列排序的成本是可以忽略不計的。在這種情況下,由于相應時間過長,所有的存儲過程都是無法在實際情況下使用的。(相應的時間各有不同,從幾秒鐘到幾分鐘不等,這要根據表的大小和所要獲得的第一個記錄而定)。其他列的索引會帶來額外的不希望出現的性能問題,例如如果你每天的導入數據很多,它有可能變得很慢。

臨時表
首先,我準備先來說一下臨時表方法,這是一個廣泛被建議使用的解決方案,我在項目中遇到過好幾次了,這里有另一篇解釋它如何工作的文章,還有一個如何在DataGrid中是用定制化分頁(Custom Paging)的例子:

ASP.NET DataGrid分頁 第二部分 – 定制化分頁

這兩篇文章中的方法都是通過把主鍵數據拷貝到臨時表中,然后對主查詢做join實現查詢優化。下面讓我們來看看這個方法的實質:
CREATE TABLE #Temp (
    ID int IDENTITY PRIMARY KEY,
    PK  /* here goes PK type */
)

INSERT INTO #Temp SELECT PK FROM Table ORDER BY SortColumn

SELECT  FROM Table JOIN #Temp temp ON Table.PK = temp.PK ORDER BY temp.ID
WHERE ID > @StartRow AND ID < @EndRow
通過把所有的行拷貝到臨時表中,我們可以對查詢進一步的優化(SELECT TOP EndRow …),但是關鍵在于最壞情況——一個包含100萬記錄的表就會產生一個100萬條記錄的臨時表。考慮到這樣的情況,再看看上面文章的結果,我決定在我的測試中放棄該方法

升序-降序
這個方法在子查詢中使用默認排序,在主查詢中使用反向排序,原理是這樣的:
DECLARE @temp TABLE (
    PK  /* PK Type */ NOT NULL PRIMARY
)

INSERT INTO @temp
SELECT TOP @PageSize PK FROM (
    SELECT TOP (@StartRow + @PageSize)
    PK,
    SortColumn /*If sorting column is defferent from the PK, SortColumn must
                 be fetched as well, otherwise just the PK is necessary */
    ORDER BY SortColumn /* default order – typically ASC */)
ORDER BY SortColumn /* reversed default order – typically DESC */

SELECT  FROM Table JOIN @Temp temp ON Table.PK = temp.PK
ORDER BY SortColumn /* default order */完整代碼:Paging_Asc_Desc

行計數
這個方法的基本邏輯依賴于SQL中的SET ROWCOUNT表達式,這樣可以跳過不必要的行并且獲得需要的行記錄
DECLARE @Sort /* the type of the sorting column */
SET ROWCOUNT @StartRow
SELECT @Sort = SortColumn FROM Table ORDER BY SortColumn
SET ROWCOUNT @PageSize
SELECT  FROM Table WHERE SortColumn >= @Sort ORDER BY SortColumn完整代碼:Paging_RowCount

子查詢
還有兩個方法也是我考慮過的,他們的來源不同。第一個是眾所周知的三角查詢(Triple Query)或者說自查詢方法,我找的一個比較透徹的方法在下面的文章中有描述

SQL Server服務器端分頁

雖然你需要訂閱,但是可以下載一個包含子查詢存儲過程定義的zip文件。列表4 SELECT_WITH_PAGINGStoredProcedure.txt文件包含一個完整的通用的動態SQL。在本文中,我也用一個類似的包含所有其他存儲過程的通用邏輯。這里的原理是連接到整個過程中,我對原始代碼做了一些縮減,因為recordcount在我的測試中不需要)
SELECT  FROM Table WHERE PK IN
    (SELECT TOP @PageSize PK FROM Table WHERE PK NOT IN
        (SELECT TOP @StartRow PK FROM Table ORDER BY SortColumn)
    ORDER BY SortColumn)
ORDER BY SortColumn完整代碼:Paging_SubQuery

游標
在看google討論組的時候,我找到了最后一個方法,你可以點這里查看原始帖子。該方法是用了一個服務器端動態游標。許多人試圖避免使用游標,因為游標沒有關系可言,以及有序性導致其效率不高,但回過頭來看,分頁其實是一個有序的任務,無論你使用哪種方法,你都必須回到開始行記錄。在之前的方法中,先選擇所有在開始記錄之前的所有行,加上需要的行記錄,然后刪除所有之前的行。動態游標有一個FETCH RELATIVE選項可以完成魔法般的跳轉。基本的邏輯如下:
DECLARE @PK /* PK Type */
DECLARE @tblPK TABLE (
    PK /* PK Type */ NOT NULL PRIMARY KEY
)

DECLARE PagingCursor CURSOR DYNAMIC READ_ONLY FOR
SELECT @PK FROM Table ORDER BY SortColumn

OPEN PagingCursor
FETCH RELATIVE @StartRow FROM PagingCursor INTO @PK

WHILE @PageSize > 0 AND @@FETCH_STATUS = 0
BEGIN
    INSERT @tblPK(PK) VALUES(@PK)
    FETCH NEXT FROM PagingCursor INTO @PK
    SET @PageSize = @PageSize - 1
END

CLOSE PagingCursor
DEALLOCATE PagingCursor

SELECT  FROM Table JOIN @tblPK temp ON Table.PK = temp.PK
ORDER BY SortColumn完整代碼:Paging_Cursor

復雜查詢的通用化
我在之前指出,所有的存儲過程都是用動態SQL實現通用性的,因此,理論上它們可以用任何種類的復雜查詢。下面有一個基于Northwind數據庫的復雜查詢例子。
SELECT Customers.ContactName AS Customer,
       Customers.Address + ', ' + Customers.City + ', ' +
                                                Customers.Country AS Address,
       SUM([Order Details].UnitPrice*[Order Details].Quantity) AS
                                                          [Total money spent]
FROM Customers
INNER JOIN Orders ON Customers.CustomerID = Orders.CustomerID
INNER JOIN [Order Details] ON Orders.OrderID = [Order Details].OrderID
WHERE Customers.Country <> 'USA' AND Customers.Country <> 'Mexico'
GROUP BY Customers.ContactName, Customers.Address, Customers.City,
         Customers.Country
HAVING (SUM([Order Details].UnitPrice*[Order Details].Quantity))>1000
ORDER BY Customer DESC, Address DESC返回第二個頁面的分頁存儲調用如下:
EXEC ProcedureName
/* Tables */
'Customers
INNER JOIN Orders ON Customers.CustomerID = Orders.CustomerID
INNER JOIN [Order Details] ON Orders.OrderID = [Order Details].OrderID',
/* PK */
'Customers.CustomerID',
/* ORDER BY */
'Customers.ContactName DESC, Customers.Address DESC',
/* PageNumber */
2,
/* Page Size */
10,
/* Fields */
'Customers.ContactName AS Customer,
Customers.Address + '', '' + Customers.City + '', '' + Customers.Country
                                                                  AS Address,
SUM([Order Details].UnitPrice*[Order Details].Quantity) AS [Total money spent]',
/* Filter */
'Customers.Country <> ''USA'' AND Customers.Country <> ''Mexico''',
/*Group By*/
'Customers.CustomerID, Customers.ContactName, Customers.Address,
 Customers.City, Customers.Country
HAVING (SUM([Order Details].UnitPrice*[Order Details].Quantity))>1000'
值得注意的是,在原始查詢中在ORDER BY語句中使用了別名,但你最好不要在分頁存儲過程中這么做,因為這樣的話跳過開始記錄之前的行是很消耗時間的。其實有很多種方法可以用于實現,但原則是不要在一開始把所有的字段包括進去,而僅僅是包括主鍵列(等同于RowCount方法中的排序列),這樣可以加快任務完成速度。只有在請求頁中,才獲得所有需要的字段。并且,在最終查詢中不存在字段別名,在跳行查詢中,必須提前使用索引列。

行計數(RowCount)存儲過程有一個另外的問題,要實現通用化,在ORDER BY語句中只允許有一個列,這也是升序-降序方法和游標方法的問題,雖然他們可以對幾個列進行排序,但是必須保證主鍵中只有一個字段。我猜如果用更多的動態SQL是可以解決這個問題的,但是在我看來這不是很值得。雖然這樣的情況很有可能發生,但他們發生的頻率不是很高。通常你可以用上面的原理也獨立的分頁存儲過程。

性能測試
在測試中,我使用了四種方法,如果你有更好的方法的話,我很有興趣知道。不管如何,我需要對這些方法進行比較,并且評估它們的性能。首先我的第一個想法就是寫一個asp.net包含分頁DataGrid的測試應用程序,然后測試頁面結果。當然,這無法反映存儲過程的真實響應時間,所以控制臺應用程序顯得更加適合。我還加入了一個Web應用程序,但不是為了性能測試,而是一個關于DataGrid自定義分頁和存儲過程一起工作的例子。這兩個應用程序都可以在 Paging Test Solution中找到。

在測試中,我使用了一個自動生成得大數據表,大概插入了500000條數據。如果你沒有一張這樣的表來做實驗,你可以點擊這里下載一段用于生成數據的表設計和存儲過程腳本。我沒有使用一個自增的主鍵列,而是用一個唯一識別碼來識別記錄的。如果我使用上面提到的腳本,你可能會考慮在生成表之后添加一個自增列,這些自增數據會根據主鍵進行數字排序,這也意味著你打算用一個帶有主鍵排序的分頁存儲過程來獲得當前頁的數據。

為了實現性能測試,我是通過一個循環多次調用一個特定的存儲過程,然后計算平均相應時間來實現的。考慮到緩存的原因,為了更準確地建模實際情況——同一頁面對于一個存儲過程的多次調用獲得數據的時間通常是不適合用來做評估的,因此,我們在調用同一個存儲過程時,每一次調用所請求的頁碼應該是隨機的。當然,我們必須假設頁的數量是固定的,10-20頁,不同頁碼的數據可能被獲取很多次,但是是隨機獲取的。

有一點我們很容易注意到,相應時間是由要獲取的頁數據相對于結果集開始的位置的距離決定的,越是遠離結果集的開始位置,就有越多的記錄要跳過,這也是我為什么不把前20也包括進我的隨機序列的原因。作為替換,我會使用2的n次方個頁面,循環的大小是需要的不同頁的數量*1000,所以,每個頁面幾乎都被獲取了1000次(由于隨機原因,肯定會有所偏差)

結果
這里有我的測試結果:Paging_Results (Excel文件)

結論
測試是按照從性能最好到最差的順序進行的——行計數、游標、升序-降序、子查詢。有一件事很有趣,通常人們很少會訪問前五頁之后的頁面,因此子查詢方法可能在這種情況下滿足你的需要,這得看你的結果集的大小和對于遠距離(distant)頁面的發生頻率預測,你也很有可能使用這些方法的組合模式。如果是我,在任何情況下,我都更喜歡用行計數方法,它運行起來十分不錯,即使對于第一頁也是如此,這里的“任何情況”代表了一些很難實現通用化的情況,在這種情況下,我會使用游標。(對于前兩頁我可能使用子查詢方法,之后再用游標方法)


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
欧美午夜精品久久久久久久| 欧美电影在线观看| 亚洲精选在线观看| 日韩免费av在线| 日本高清久久天堂| 国产精品成人免费视频| 国精产品一区一区三区有限在线| 日韩av在线精品| 欧美资源在线观看| 国产91在线播放精品91| 啪一啪鲁一鲁2019在线视频| 亚洲视频在线免费观看| 亚洲激情国产精品| 91久久中文字幕| 欧美激情区在线播放| 中文字幕久久亚洲| 日韩在线观看免费全| 亚洲韩国日本中文字幕| 国产精品欧美在线| 欧美福利视频在线观看| 免费av在线一区| 浅井舞香一区二区| 久久久久国产精品www| 综合欧美国产视频二区| 亚洲国产精品久久久久秋霞不卡| 一区二区三区国产视频| 这里只有精品久久| 久久久精品免费| 亚洲欧洲激情在线| 国产成人精品a视频一区www| 亚洲黄色片网站| 亚洲精品日韩在线| 日韩在线观看免费高清完整版| 欧美日韩国产区| 亚洲欧美精品在线| 色av吧综合网| 色妞色视频一区二区三区四区| www日韩欧美| 色狠狠av一区二区三区香蕉蜜桃| 日韩av123| 国产主播在线一区| 国产欧美日韩中文字幕在线| 久久视频在线看| 国产精品69久久久久| 国产成人福利网站| 日韩人体视频一二区| 国产亚洲视频中文字幕视频| 欧美激情视频给我| 亚洲人成在线电影| www.亚洲男人天堂| 亚洲自拍小视频| 成人国产在线视频| 91九色在线视频| 国产欧美精品一区二区三区介绍| 国产中文字幕亚洲| 国产精品久久久久久久久久小说| 国产精品白嫩美女在线观看| 久久亚洲国产精品成人av秋霞| 久久在线精品视频| 欧美视频免费在线观看| 久久成人人人人精品欧| 国产中文欧美精品| 国产精品久久久久久搜索| …久久精品99久久香蕉国产| 国产婷婷成人久久av免费高清| 日韩精品在线播放| 亚洲最大成人免费视频| 亚洲精品视频中文字幕| 国产小视频91| 日韩精品视频免费在线观看| 国产中文字幕亚洲| 国产成人精品av在线| 色综合久久精品亚洲国产| 亚洲国产精品va| 亚洲美女在线观看| 68精品国产免费久久久久久婷婷| 国产日韩av在线| 欧美成人免费va影院高清| 精品国内产的精品视频在线观看| 最近免费中文字幕视频2019| 2021久久精品国产99国产精品| 久久伊人精品一区二区三区| 欧洲美女免费图片一区| 91久久久久久久一区二区| 97在线观看视频国产| 日韩国产在线播放| 欧美日韩一区二区三区在线免费观看| 91精品国产综合久久香蕉922| 日韩黄在线观看| 亚洲跨种族黑人xxx| 欧美日韩激情小视频| 日韩在线观看视频免费| 欧美精品videosex性欧美| 欧美高清视频在线播放| 久久九九精品99国产精品| 欧美国产精品va在线观看| 韩国19禁主播vip福利视频| 色七七影院综合| 欧美成人剧情片在线观看| 欧美一级成年大片在线观看| 日韩精品www| 欧美裸体男粗大视频在线观看| 欧美极品美女视频网站在线观看免费| 成人av在线网址| 色视频www在线播放国产成人| 国产一区二区三区中文| 久国内精品在线| 日韩成人中文字幕在线观看| 欧美网站在线观看| 91中文精品字幕在线视频| 久久国产精品99国产精| 国产精品一区二区久久久久| 欧美日韩免费看| 粉嫩老牛aⅴ一区二区三区| 亚洲精品成人免费| 欧美日韩成人黄色| 97成人在线视频| 亚洲电影av在线| 九色精品美女在线| 中文字幕精品www乱入免费视频| 欧美视频13p| 亚洲色图欧美制服丝袜另类第一页| 日韩美女免费视频| 精品久久久久久中文字幕一区奶水| 一夜七次郎国产精品亚洲| 欧美激情一区二区三区久久久| 欧美成人一区在线| 亚洲福利视频二区| 国产精品欧美日韩| 欧美自拍视频在线| 欧美中文字幕视频| 5278欧美一区二区三区| 色青青草原桃花久久综合| 精品成人69xx.xyz| 欧美性猛交xxxx免费看漫画| 久久久久久久久久久国产| 国产欧美日韩精品在线观看| 日韩在线免费高清视频| 成人激情春色网| 欧美成人sm免费视频| 欧美在线观看网站| 成人av电影天堂| 欧美激情综合色综合啪啪五月| 国产精品久久视频| 亚洲二区在线播放视频| 欧美成人中文字幕在线| 91香蕉嫩草神马影院在线观看| 亚洲视频999| 岛国av一区二区在线在线观看| 亚洲综合成人婷婷小说| 欧美在线视频网| 国产成一区二区| 精品久久久视频| 日韩精品福利网站| 97碰在线观看| 久久人91精品久久久久久不卡| 亚洲无线码在线一区观看| 亚洲午夜精品久久久久久久久久久久| 欧美高清无遮挡| 国产免费久久av| 午夜精品久久久久久久久久久久| 97免费中文视频在线观看| 一区二区三区视频免费在线观看| 国产在线播放不卡|