[移文]DataList 分頁(運用 T-SQL Paging)
這個問題的答案是肯定的!而且其實不難,主要的重點就是從資料來源下手,畢竟資料繫結控制項 (Data-Bound Controls) 處理的就是資料來源裡的資料,也就是說,你給它甚麼資料(集),它就呈現甚麼,舉例來說,你希望每頁呈現 10 筆資料,那就真的查詢 10 筆資料出來。當然你隨便搜尋一下 "DataList 分頁" 就可以查到很多詳細範例,但絕大多數都是用 PagedDataSource 做的,本文的目的是利用 T-SQL 分頁(註),以更有效率的方式來解決分頁問題。
廢話不多說,直接來寫看看,配置第一個下拉選單指定每頁顯示資料筆數,第二個下拉選單則是顯示欄位數,然後是 DataList、上一頁、下一頁巡覽按鈕、頁次提示文字等。參考底下原始檔:
分頁筆數:<asp:DropDownList ID="DropDownList1" runat="server" AutoPostBack="True" OnSelectedIndexChanged="DropDownList1_SelectedIndexChanged">
<asp:ListItem>5</asp:ListItem>
<asp:ListItem>10</asp:ListItem>
<asp:ListItem>20</asp:ListItem>
<asp:ListItem>50</asp:ListItem>
<asp:ListItem>100</asp:ListItem>
</asp:DropDownList>
資料欄數:<asp:DropDownList ID="DropDownList2" runat="server" AutoPostBack="True"
OnSelectedIndexChanged="DropDownList2_SelectedIndexChanged">
<asp:ListItem>1</asp:ListItem>
<asp:ListItem Selected="True">5</asp:ListItem>
<asp:ListItem>10</asp:ListItem>
<asp:ListItem>20</asp:ListItem>
</asp:DropDownList>
<asp:SqlDataSource ID="SqlDataSource1" runat="server" ConnectionString="<%$ ConnectionStrings:NorthwindConnectionString %>">
</asp:SqlDataSource>
<asp:DataList ID="DataList1" runat="server" DataSourceID="SqlDataSource1" BorderColor="Gray"
BorderStyle="Solid" BorderWidth="1px" OnItemDataBound="DataList1_ItemDataBound"
RepeatColumns="10">
<ItemTemplate>
<div style="border: solid 1px #0000ff;">
No.:
<asp:Label ID="NoLabel" runat="server" Text='<%# Eval("NO") %>' />
<br />
CategoryName:
<asp:Label ID="CategoryNameLabel" runat="server" Text='<%# Eval("CategoryName") %>' />
<br />
ProductName:
<asp:Label ID="ProductNameLabel" runat="server" Text='<%# Eval("ProductName") %>' />
<br />
QuantityPerUnit:
<asp:Label ID="QuantityPerUnitLabel" runat="server" Text='<%# Eval("QuantityPerUnit") %>'></asp:Label>
<br />
UnitsInStock:
<asp:Label ID="UnitsInStockLabel" runat="server" Text='<%# Eval("UnitsInStock") %>'></asp:Label>
<br />
Discontinued:
<asp:Label ID="DiscontinuedLabel" runat="server" Text='<%# Eval("Discontinued") %>' />
<br />
<br />
</div>
</ItemTemplate>
<FooterTemplate>
<asp:Button ID="btnPrevious" runat="server" AccessKey="P" Text="< 上一頁" OnClick="btnPrevious_Click" />
<asp:Button ID="btnNext" runat="server" AccessKey="N" Text="下一頁 >" OnClick="btnNext_Click" />
<asp:Literal ID="litIndicator" runat="server" EnableViewState="False"></asp:Literal>
</FooterTemplate>
</asp:DataList>
</div>
T-SQL 分頁的部分,用 Northwind 裡的 Products by Category 檢視表為例,要取得指定範圍的資料大概會用如下的查詢指令 (其中的參數 @start、@end 會在網頁程式碼動態指定):
[NO],
CategoryName,
ProductName,
QuantityPerUnit,
UnitsInStock,
Discontinued
FROM (
SELECT (ROW_NUMBER() OVER (ORDER BY CategoryName)) 'NO', *
FROM [Products by Category]
)
AS [p]
WHERE ([NO] BETWEEN @start AND @end);
程式碼需處理的地方,底下列出兩個關鍵:
- 上一頁、下一頁按鈕需巡覽至正確頁次,所以要算出查詢的起始與結束範圍,相關程式碼如下:
/// 動態繫結 DataList 呈現資料 /// </summary> private void BindList() { // 目前頁次 int currentPage = (Session["page"] == null) ? 1 : (String.IsNullOrEmpty(Session["page"].ToString())) ? 1 : Convert.ToInt32(Session["page"].ToString()); // 透過下拉選單 1 指定每頁筆數 int pageSize = Convert.ToInt32(this.DropDownList1.SelectedValue); // 透過下拉選單 2 指定顯示欄數 this.DataList1.RepeatColumns = Convert.ToInt32(this.DropDownList2.SelectedValue); // 計算起始與結束範圍 int startNo = 1 + ((currentPage - 1) * pageSize); int endNo = startNo + pageSize - 1; // 動態指定 SQL 查詢 this.SqlDataSource1.SelectParameters.Clear(); this.SqlDataSource1.SelectCommand = "SELECT * FROM (SELECT (ROW_NUMBER() OVER (ORDER BY CategoryName)) 'NO', * FROM [Products by Category]) AS [p] WHERE ([NO] BETWEEN @start AND @end) "; this.SqlDataSource1.SelectParameters.Add("@start", System.Data.DbType.Int32, startNo.ToString()); this.SqlDataSource1.SelectParameters.Add("@end", System.Data.DbType.Int32, endNo.ToString()); // 回寫 Session 紀錄目前頁次 Session["page"] = currentPage; } protected void btnPrevious_Click(object sender, EventArgs e) { // 由目前頁次巡覽上一頁 int currentPage = (Session["page"] == null) ? 1 : (String.IsNullOrEmpty(Session["page"].ToString())) ? 1 : Convert.ToInt32(Session["page"].ToString()); Session["page"] = (currentPage == 1) ? currentPage : currentPage - 1; BindList(); } protected void btnNext_Click(object sender, EventArgs e) { // 由目前頁次巡覽下一頁 int currentPage = (Session["page"] == null) ? 1 : (String.IsNullOrEmpty(Session["page"].ToString())) ? 1 : Convert.ToInt32(Session["page"].ToString()); Session["page"] = currentPage + 1; BindList(); }
- 第一頁沒有上一頁功能,最後一頁沒有下一頁功能,所以要判斷其停用條件:
{ if (e.Item.ItemType == ListItemType.Footer) { int currentPage = Convert.ToInt32((Session["page"] == null) ? "1" : (String.IsNullOrEmpty(Session["page"].ToString())) ? "1" : Session["page"].ToString()); int pageSize = Convert.ToInt32(this.DropDownList1.SelectedValue); // 判斷是否顯示上一頁、下一頁按鈕 if (currentPage == 1) { ((Button)e.Item.FindControl("btnPrevious")).Enabled = false; } if (this.DataList1.Items.Count < pageSize) { ((Button)e.Item.FindControl("btnNext")).Enabled = false; } // 顯示頁次資訊 ((Literal)e.Item.FindControl("litIndicator")).Text = "頁次:" + currentPage.ToString(); } }
大致如此,需特別說明的是,本例利用 Session 存放目前頁次,若用其他方式存放,例如:隱藏欄位(或 ViewState)、QueryString...等,應該也是 OK 的,至於優缺點則不在本文討論範圍,或許大家可以回應一些意見,我很樂意與大家討論。最後附上內含 C#、VB.NET 版完整程式碼,有需要的人可以下載測試,若有寫得不好的地方,也希望能提供點建議給我,謝謝!
PagedDataList.rar
※ 探討 T-SQL 分頁幾篇不錯的文章
[SubText心得]ASP.NET TSQL 效能較佳分頁方法及友善 SEO 的分頁
用 SQL Server 2005 做分頁查詢
Paging of Large Resultsets in ASP.NET