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 * floorcount) //当当前序号小于倒数第二页页码时显示在后端...
    8 {
    9 strBuilder.AppendFormat(GetAHtml("", string.Format(url, 10 * (pagecount + 1) + 1), ""));
    10 }    

          如果能够把这些逻辑搞清楚了,分页也就不算太难了!

       下面是全部代码:  

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    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.思路很重要                             

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

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

    [02] 学习的那些事
    在学习中深入,在实践中提高
     
     
     
     
     
     
     
     


  • 相关阅读:
    Linux下使用mail发送邮件
    Thirft框架介绍
    当谈论设备指纹时,我们到底在说什么?(转)
    设备指纹(Device Fingerprinting)是什么?
    搭建RESTful API 之 实现WSGI服务的URL映射
    OpenStack设计与实现5——RESTful API和WSGI
    Charles使用
    iBeacon室内定位原理解析【转】
    docker安装
    chase
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/2417137.html
Copyright © 2011-2022 走看看