zoukankan      html  css  js  c++  java
  • 学习之路九:深入剖析Web分页原理

        说一下今天天气很好,心情也非常的不错,写起来也非常舒畅,也希望园友们每天好心情,为自己的人生目标努力着!

      这段时间因为项目需要,要做一个分页的功能,说实话这类的文章在园子里面可以说是满天飞了,为什么要写呢?没什么高深的技术,只是做个总结,把那些零零碎碎的问题整合起来,好给大家一个完整的参考吧!

      这是我第一次自动动手写分页,所以这样的文章适合跟我一样的小菜阅读....

     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他们的分页思想,找不到那篇文章了,找到了园友发个链接给我!

        我也是看到这些文章做个小总结,没什么创新!    

    1. 每一张表设置固定的容量,达到一定程度后,把新纪录转移到另外一张表中,让以前的那些表成为历史数据!
    2. 做好查询需要的索引,虽然不能滥用索引,但是适当的增加将加快查询速度!
    3. 优化查询,避免表扫描,少用模糊查询,也就是在没有索引的前提下,扫描整张表!
    4. 在服务端代码中尽量考虑到数据缓冲和连接池的情况!
    5. 对于千万级的数据,可以参考百度,Goole等网站的分页技巧!
    6. 其实它们的分页利用了客户只关注前面重要的信息,越往后就不会太多的关注的思维定势!
    7. 千万级的数据,只取出前面几十万的信息,然后进行排序分页! 

      

     5.思路很重要                             

      我总觉得在编程之前应该就把思路理清楚,清楚之后就能行如流水,但是现在的我还没有到达这个境界,需要多多努力!

      好了,差不多就是这么多了,也算对分页有了一点点的理解了,不至于以后工作需要而手忙脚乱的,写在这边与大家一起分享! 

  • 相关阅读:
    Spring AOP里的静态代理和动态代理,你真的了解嘛?
    redis的三种启动方式
    Linux MySQL分库分表之Mycat
    Linux 之Mycat搭建报错 java.net.MalformedURLException: Local host name unknown: java.net.UnknownHostException
    Linux MySQL Proxy 读写分离
    Linux MySQL集群搭建之主从复制
    Linux 虚拟机详细安装MySQL
    MySQL 性能优化细节
    MySQL 性能优化之慢查询
    MySQL事务处理
  • 原文地址:https://www.cnblogs.com/yangcaogui/p/2415443.html
Copyright © 2011-2022 走看看