DataGrid 是功能非常強大的 ASP.NET Web 伺服器端控制項,除能方便按各種方式格式化顯示表格資料,還可對表格資料動態排序、編輯與分頁。使 Web 開發人員從繁瑣程式碼中解脫。實現 DataGrid 分頁功能一直是很多初學 ASP.NET 人感到棘手問題,特別是自訂分頁功能,實現方法多種多樣、非常靈活。本文介紹一種 DataGrid 控制項在 Access 資料庫下的快速分頁法,幫助初學者掌握 DataGrid 分頁技術。
DataGrid 內建分頁方法使用類似 "SELECT * FROM <TABLE>" SQL 陳述式從資料庫表取出所有記錄到 DataSet,DataGrid 控制項繫結該 DataSet 後,自動分頁功能會幫你從 DataSet 篩選出目前分頁資料並顯示,其他無用資料被捨棄。
另一種方法用自訂分頁功能,先將 DataGrid AllowCustomPaging 設為 True,再利用 DataAdapter Fill 方法將資料篩選工作提前到填充 DataSet 時,而非讓 DataGrid 幫你篩選:
public int Fill (
DataSet dataSet, //要填充 DataSet。
int startRecord, //從其開始從零開始記錄號。
int maxRecords, //要檢索最大記錄數。
string srcTable //用於表對應源表名稱。
);
該方法先將查詢結果填充到 DataSet,再捨棄不需顯示資料。當然,自訂分頁功能需完成事情不止這些,本文後面詳細介紹。
以上兩種方法工作原理都是先從資料庫取出所有記錄,再篩選有用資料顯示。可見兩方法效率基本一致,因它們在資料存取階段未採取有效措施減少 Access 對磁碟存取次數。對少量記錄此開銷可能較小,若針對大量資料分頁,開銷將非常巨大,導致分頁速度極慢。換句話說,就算每個 DataGrid 分頁面只顯示幾萬條記錄資料庫表其中 10 條,每次 DataGrid 分頁仍要從該表取出所有記錄。
許多人已意識此問題,提出解決方法:用自訂分頁,每次只從資料庫取出要顯示資料。這樣,我們需在 SQL 陳述式上下功夫。因 Access 不支援真正預存程序,編寫分頁演算法上沒有 SQL Server 那麼自由。SQL Server 可在預存程序利用暫存表實現高效率分頁演算法,受廣泛採用。對 Access,我們必須想辦法在一條 SQL 陳述式內實現最高效演算法。
用一條 SQL 陳述式取得某段資料方法有好幾種。演算法不同,效率也不同。我經粗略測試,發現效率最差 SQL 陳述式執行耗時大約是效率最高 SQL 陳述式 3 倍!且此數值會隨記錄總數增加而增加。下面介紹其中兩條常用 SQL 陳述式。
為方便討論,先約定如下:
| 變數 | 說明 | 變數 | 說明 |
|---|---|---|---|
| @PageSize | 每頁顯示記錄總數 | @MiddleIndex | 中間頁索引 |
| @PageCount | 分頁總數 | @LastIndex | 最後一頁索引 |
| @RecordCount | 資料表記錄總數 | @TableName | 資料庫表名稱 |
| @PageIndex | 目前頁索引 | @PrimaryKey | 主鍵欄位名稱 |
| @FirstIndex | 第一頁索引 | @QueryFields | 要查詢欄位集合 |
變數定義:
@PageCount (int)Math.Ceiling((double)@RecordCount / @PageSize)
@FirstIndex 0
@LastIndex @PageCount – 1
@MiddleIndex (int)Math.Ceiling((double)@PageCount / 2) – 1
先看效率最差 SQL 陳述式:
SELECT TOP @PageSize * FROM @TableName
WHERE @PrimaryKey NOT IN (
SELECT TOP @PageSize*@PageIndex @PrimaryKey FROM @TableName
ORDER BY @PrimaryKey ASC
) ORDER BY @PrimaryKey ASC
此 SQL 慢在 NOT IN 這裡,主 SELECT 遍歷每個 @PrimaryKey 值都要跟子 SELECT 結果集每個 @PrimaryKey 值比較,時間複雜度極大。提醒大家,平時寫 SQL 應盡量避免用 NOT IN,因它常增加整個 SQL 時間複雜度。
另一種用兩個 TOP 和三個 ORDER BY SQL 陳述式:
SELECT * FROM (
SELECT TOP @PageSize * FROM (
SELECT TOP @PageSize*(@PageIndex+1) * FROM @TableName
ORDER BY @PrimaryKey ASC
) TableA ORDER BY @PrimaryKey DESC
) TableB ORDER BY @PrimaryKey ASC
此 SQL 空間複雜度較大。若要顯示分頁剛好是最後一頁,效率比直接 SELECT 所有記錄還低。因此對分頁演算法,應具體情況具體分析,不可一概而論。下面簡單介紹相關概念,若您對主鍵與索引非常熟悉可直接跳過。
在 ACCESS 中,一個表主鍵(PRIMARY KEY,又稱主索引)必然是唯一索引(UNIQUE INDEX),其值不重複。除此之外,索引依據索引欄位值排序,每個索引記錄包含指向它所引用資料列指標,這對 ORDER BY 執行非常有幫助。我們可利用主鍵這兩特點實現對某條記錄定位,從而快速取出某分頁上要顯示記錄。
舉例,假設主鍵欄位為 INTEGER 型,資料庫表中記錄索引已按主鍵值升序排好(預設情況),則主鍵值為 "11" 記錄索引,肯定剛好在值為 "12" 記錄索引前面(假設資料庫表存在主鍵值 "12" 記錄)。若主鍵欄位不具 UNIQUE 約束,資料庫表將可能存在兩個或以上主鍵值 "11" 記錄,這樣無法確定這些記錄間前後位置。
下面讓我們看看如何利用主鍵進行資料分段查詢吧。