說一下今天天氣很好,心情也非常的不錯,寫起來也非常舒暢,也希望園友們每天好心情,為自己的人生目標努力着!
這段時間因為項目需要,要做一個分頁的功能,說實話這類的文章在園子里面可以說是滿天飛了,為什么要寫呢?沒什么高深的技術,只是做個總結,把那些零零碎碎的問題整合起來,好給大家一個完整的參考吧!
這是我第一次自動動手寫分頁,所以這樣的文章適合跟我一樣的小菜閱讀....
1.分頁的最基本參數
總頁數 → PageTotalCount //查詢的結果分頁之后有多少頁 總記錄數 → RecordTotalCount //查詢的結果包含多少條記錄 每一頁的記錄數 → DisplayRecordCount //每一頁顯示多少條記錄數 一次顯示多少頁數 → DisplayPageCount //一次加載顯示多少個頁數,五個或者十個等等 首頁和末頁 → FirstPage ,LastPage //快速回到第一頁以及最后一頁的按鈕 上一頁和下一頁 → UpPage,NextPage //下一頁和上一頁 頁索引號 → IndexPage //用戶當前點擊的頁號碼
Note:這只是最基本的參數,還有很多的查詢參數需要我們按照自己的項目需求來定義!
Note:對於總記錄數的獲取我的方案是使用輸出參數來獲取!
2.在URL地址中傳入分頁參數實現分頁
2.1 原理:
主要是通過獲取URL中的參數值來判斷用戶點擊的是第幾頁!
2.2 機制:
通過查看HTML代碼,發現每一個分頁按鈕的超鏈接都是這樣寫的,如:
博客園分頁源代碼:
所以這種的方式還是比較常用的!
2.3 原理分析:
①所有的HTML的代碼在后台進行組裝,然后在頁面加載的時候進行輸出!
②每個超鏈接的href屬性都在后台進行賦值!
③對一些復雜的業務邏輯進行有效處理!
2.4 解決點擊“...”按鈕的問題,說實話這里面的邏輯還是有點復雜的,我搞了好久才弄清楚了:
這邊涉及到一個層的概念(不是標准術語),所謂的層就是一個頁面一次性顯示的的總頁數!
Note: 層的層次關系:
1,2,3 … 10; 第一層 索引號為“0”
11,12,13 … 20; 第二層 “1”
21,22,23 … 30; 第三層 “2”
如果說你的總頁數有98頁,每一次顯示10頁,那么分頁的總層數就為 → int pageLevelCount = 98 % 10 ==0 ? 98 / 10 : 98 / 10 + 1
有如下幾個核心代碼:
1 int currentPageLevel = pageIndex / 10; //當前頁所在的層數
2 //過濾一下整數,如10,20,30 本來他們應該屬於自己的層數,但是通過上面的計算會增加一個層數,所以要過濾下
3 currentPageLevel = pageIndex % 10 == 0 ? currentPageLevel - 1 : currentPageLevel;
4 int pageTotalLevelCount = pageTotalCount / 10; //總層數,如果不是10的倍數,就會少一層,主要用於后面“…”做判斷的
5 //計算后面三個點“...”處於什么樣的頁索引號,如果當前頁+1 乘以10大於總頁數,那么說明后面沒有了!
6 //下面的變量判斷當前的頁層數是不是最后一個頁層數!
7 int currentPageLastLevel = 10 * (currentPageLevel + 1) > pageTotalCount ? pageTotalCount : 10 * (currentPageLevel + 1);
下面的代碼是判斷頁面上時候有“...”的按鈕:
1 //對前面“...”按鈕的判斷
2 if (pageIndex > 10) //對也按鈕的設置
3 {
4 strBuilder.AppendFormat(GetAHtml("", string.Format(url, 10 * pagecount), "..."));
5 }
6 //后面顯示“...”按鈕的判斷
7 if (pageIndex <= 10 * pageTotalLevelCount) //當當前頁索引號小於倒數第二頁頁碼時顯示在后端...
8 {
9 strBuilder.AppendFormat(GetAHtml("", string.Format(url, 10 * (pagecount + 1) + 1), ""));
10 }
Note:上面的代碼和下面的代碼不是一起的,上面的是我做演示的,下面的是一個完整的方案,寫的還不怎么好,明天我貼一個修改后的方案!
如果能夠把這些邏輯搞清楚了,分頁也就不算太難了!
下面是全部代碼:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; using System.Data.SqlClient; using System.Data; using System.Text.RegularExpressions; using System.Text; namespace Web分頁原理學習Demo { public partial class _Default : System.Web.UI.Page { public int totalCount = 0; public int pageIndex = 1; public string pageHTML = ""; string url = HttpContext.Current.Request.Url.AbsoluteUri;//當前頁面絕對路徑 protected void Page_Load(object sender, EventArgs e) { string QueryStringName = Request.QueryString["page"]; //QueryStringName = QueryStringName == null ? "1" : QueryStringName; //首先先獲取URL //"http://localhost:1033/Default.aspx" pageIndex = QueryStringName == null ? 1 : Convert.ToInt32(QueryStringName); //對URL進行一些設置 if (url.Contains("aspx?")) { if (Regex.IsMatch(url, @"page=[0-9]*$", RegexOptions.IgnoreCase))//如果存在page=*的字符串 { url = Regex.Replace(url, @"page=[0-9]*$", "", RegexOptions.IgnoreCase);//替換掉page=*的字符串 } url += "page" + "={0}"; } else { url += "?" + "page" + "={0}"; } //if (!url.Contains("page")) //{ // url += "?page={0}"; //} this.Repeater1.DataSource = GetData(pageIndex); this.Repeater1.DataBind(); pageHTML = GetPageHTML(totalCount, pageIndex, url); } public DataTable GetData(int pageIndex) { DataTable dt = new DataTable(); string connString = @"server=22610800E100468;integrated security = SSPI;database=Northwind"; using (SqlConnection conn = new SqlConnection(connString)) { SqlCommand cmd = conn.CreateCommand(); cmd.CommandText = "MyPage"; cmd.Parameters.Add(new SqlParameter("@pageIndex", pageIndex)); SqlParameter para = new SqlParameter(); para.ParameterName = "@recordTotalCount"; para.DbType = DbType.Int32; //必須指明參數類型 para.Direction = ParameterDirection.Output; cmd.Parameters.Add(para); cmd.CommandType = System.Data.CommandType.StoredProcedure; SqlDataAdapter da = new SqlDataAdapter(cmd); da.Fill(dt); totalCount = (int)da.SelectCommand.Parameters["@recordTotalCount"].Value; } return dt; } //開始填充HTML代碼 public string GetPageHTML(int recordTotalCount, int pageIndex, string url) { StringBuilder strBuilder = new StringBuilder(1000); string attr = ""; int pagecount = 0;//當前頁面的總層數 int floorcount = 0;//分頁的總層數 int currentLastPage = 0;//當前最后一頁的頁碼,用來保存最后一頁的號碼 //總頁數 int pageTotalCount = recordTotalCount / 10 + 1; strBuilder.Append("<div>"); attr = pageIndex == 1 ? "visible=\"" + "false\"" : ""; //標志當前頁第一頁是否相等 來控制前倆個按鈕的有效性 strBuilder.AppendFormat(GetAHtml(attr, string.Format(url, 1), "首頁")); strBuilder.AppendFormat(GetAHtml(attr, string.Format(url, pageIndex - 1), "上一頁")); pagecount = pageIndex / 10;//當前頁數 0~1~2 pagecount = pageIndex % 10 == 0 ? pagecount - 1 : pagecount;//清除當 當前頁數為分頁頁碼數的整數倍頁時除數多一的狀況 floorcount = pageTotalCount / 10;//頁面層數 0~1~2 currentLastPage = pageTotalCount < 10 * (pagecount + 1) ? pageTotalCount : 10 * (pagecount + 1); if (pageIndex > 10) //對也按鈕的設置 { strBuilder.AppendFormat(GetAHtml("", string.Format(url, 10 * pagecount), "...")); } for (int i = 10 * pagecount + 1; i < currentLastPage; i++) { if (i == pageIndex) { strBuilder.AppendFormat(GetSpanHtml(i, "")); } else { strBuilder.AppendFormat(GetAHtml("", string.Format(url, i), i.ToString())); //設置超鏈接 } } if (pageIndex <= 10 * floorcount) //當當前序號小於倒數第二頁頁碼時顯示在后端... { strBuilder.AppendFormat(GetAHtml("", string.Format(url, 10 * (pagecount + 1) + 1), "")); } attr = pageIndex == pageTotalCount ? "visible=\"" + "false\"" : "";//標志當前頁最后一頁是否相等 來控制后倆個按鈕的有效性 strBuilder.AppendFormat(GetAHtml(attr, string.Format(url, pageIndex + 1), "下一頁")); strBuilder.AppendFormat(GetAHtml(attr, string.Format(url, pageTotalCount), "末頁")); strBuilder.AppendFormat("</div>"); return strBuilder.ToString(); } /// <summary> /// get the html of a label /// </summary> /// <param name="title">a's title</param> /// <param name="url">the url of a</param> /// <param name="attr">the attribute</param> /// <returns>return html string</returns> private static string GetAHtml(string attr, string url, string title) { return "<a " + attr + " href=\"" + url + "\" style=\"margin-right:5px;\">" + title + "</a>\n"; } //這個方法的目的是固定住當前用戶點擊的頁索引 private static string GetSpanHtml(int num, string className) { return "<span class=\"" + className + "\">" + num + "</span>\n"; } } }
3.分頁存儲過程深入學習
3.1 定義表變量來存儲數據,實現分頁
--使用表變量來讀取數據,注意表變量的語法,定義一個自動那個增長列用來以后的分頁
DECLARE @MyTable TABLE (myID INT IDENTITY(1,1),OrderID INT,CustomerID NCHAR(5))
--把你刪選的數據填充到表變量中去
INSERT INTO @MyTable(OrderID,CustomerID)
(
SELECT OrderID,CustomerID FROM dbo.Orders
)
SELECT myID,OrderID,CustomerID FROM @MyTable WHERE myID BETWEEN 1 AND 10
Note:自SQL Server2005之后出來了“CTE”的語法,大家也可以使用這種方式來進行分頁!
1 WITH MyTable(ID,Number)
2 AS
3 (
4 SELECT AppID,ROW_NUMBER() OVER(ORDER BY AppID) FROM Core
5 )
6
7 SELECT * FROM MyTable WHERE Number BETWEEN 10000 AND 10010
3.2 使用“Top”和“In”
1 --使用“TOP”跟“IN”語法
2 SELECT TOP 10 OrderID FROM dbo.Orders WHERE OrderID NOT IN
3 (
4 SELECT TOP 10 OrderID FROM dbo.Orders
5 )
6 //這個語句就可以查處表中第“11”條到“20”的數據了!
3.3 使用“Row_Number() Over(Order By [字段名] DESC)”
1 --這是SQL Server2005新出來的函數,不僅操作簡單,而且還易於理解!
2 --所以了解這個函數的語法就變得很重要了!
3 SELECT OrderID,CustomerID FROM
4 (
5 SELECT OrderID,CustomerID, ROW_NUMBER() OVER(ORDER BY OrderID) AS number FROM dbo.Orders
6 ) AS T
7 WHERE T.number BETWEEN 1 AND 10
Note:你可以指定你要排序的主鍵是按升序還是按降序排列!ASC → 升序,DESC → 降序!
3.4 使用“Top”和“Max”
1 --分頁思想:
2 --首先根據主鍵進行排序,刪選出前十條,默認是降序排列
3 --然后取出前十條的最大值,也就是前十條的最后一條記錄
4 --最后在刪選出這條記錄的后十條記錄,那么就是“11”到第“20”條的記錄了
5 SELECT TOP 10 OrderID,CustomerID FROM dbo.Orders AS T WHERE T.OrderID >
6 (
7 SELECT MAX(TempTable.OrderID) FROM
8 (SELECT TOP 10 OrderID FROM dbo.Orders ORDER BY OrderID) AS TempTable
9 )
10 //默認為升序 → ASC
3.5 分頁查詢速度比較
說實話這些比較園子里面也很多,我就做個總結了,不實際測試了!
Top,Max > Row_Number > Top > 表變量 !
3.6 比較“Top Max”和“Row Number”的性能差異所在
如果對“SQL 執行計划”還沒有一定的理解,請先看這篇文章:看懂SqlServer查詢計划 ,值得一看的文章!
從上面的可以看出,在單表分頁的情況下,“Row_Number”比“Top ,Max”會多檢索出很多行,那么在性能上“Top Max”就比“Row_Number”好點!
Note:這種情況只限於單表操作的情況下,如果說是多表查詢,感覺還是用“Row_Number”會比較好點,因為在多表的情況下,“Top,Max”或做出兩次的連接查詢,在大數據量的情況下,性能會比“Row_Number”差一點!如圖:
4.大數據的分頁思想
前幾天看到一片文章是說關於百度,Google他們的分頁思想,找不到那篇文章了,找到了園友發個鏈接給我!
我也是看到這些文章做個小總結,沒什么創新!
- 每一張表設置固定的容量,達到一定程度后,把新紀錄轉移到另外一張表中,讓以前的那些表成為歷史數據!
- 做好查詢需要的索引,雖然不能濫用索引,但是適當的增加將加快查詢速度!
- 優化查詢,避免表掃描,少用模糊查詢,也就是在沒有索引的前提下,掃描整張表!
- 在服務端代碼中盡量考慮到數據緩沖和連接池的情況!
- 對於千萬級的數據,可以參考百度,Goole等網站的分頁技巧!
- 其實它們的分頁利用了客戶只關注前面重要的信息,越往后就不會太多的關注的思維定勢!
- 千萬級的數據,只取出前面幾十萬的信息,然后進行排序分頁!
5.思路很重要
我總覺得在編程之前應該就把思路理清楚,清楚之后就能行如流水,但是現在的我還沒有到達這個境界,需要多多努力!
好了,差不多就是這么多了,也算對分頁有了一點點的理解了,不至於以后工作需要而手忙腳亂的,寫在這邊與大家一起分享!