我们在页面展示一个table表格的时候,当数据量较大时,常常会考虑到数据分页的问题,数据分页一般有三种方式,分别是前端数据分页,后端数据分页,数据库分页。

前端数据分页:是把所有数据加载到前端,然后在前端用js实现数据的分页,这种分页方式是最方便简单的,但是也是效果最差的。因为当数据量很大时,前端加载数据耗时很久,页面会因为加载数据而无法正常显示,体验很差。并且因为数据是一次性加载到前端,当数据库有新数据更新时,如果不刷新页面重新读取数据库,就无法获取最新数据。

后端数据分页:是在后端将数据全部查出来,,将前端需要显示的部分数据筛选出来,传回前端。这种方式相对于前端数据分页来说效果要好很多。前端只用回传当前页要显示的少量数据,前端接收到的数据量也很少,传输负担就会小很多,可以减轻页面加载延迟。后端数据分页的缺点和前端刷新一样,如果想获取新数据,就要重新从数据库中加载所有数据到后端。

数据库分页:数据库分页是最好的分页方式,在后端拼接查询sql的时候,就把前端需要展示部分的数据条件拼接出来。每次都重新查询数据库,可以获取到最新数据,每次从数据库查询的数据量,只有前端当前要显示的那一部分,大大减少了数据的传输,缺点是sql的拼接比较复杂。

作者对三种分页方式都进行了总结,分为三篇文章。本文为数据库分页方法,举例了Sql Server和Oracle数据库两种分页sql拼接方法。

一、Sql查询条件的拼接

方法分Sql Server和Oracle两种,主要是因为Sql Server和Oracle的语法有差异,这个方法主要用于拼接Select *  From Table Where A = B and C = D

/// <summary>
        /// 拼接查询条件(包含时间)
        /// </summary>
        /// <param name="dic"></param>
        /// <returns></returns>
        public static string SqlStrWithTime(Dictionary<string, string> dic)
        {
            string SqlStr = "";
            //判断数据库类型
            if (enum_dbType == Oracle)
            {
                if (dic.Count != 0)
                {
                    SqlStr = " where  1 = 1 and ";
                    foreach (var item in dic)
                    {
                        if (item.Key == "begintime")
                        {
                            SqlStr += "RecordTime >=to_date('" + item.Value + "','YYYY-MM-DD HH24:mi:ss') and ";
                        }
                        else if (item.Key == "endtime")
                        {
                            SqlStr += "RecordTime <=to_date('" + item.Value + "','YYYY-MM-DD HH24:mi:ss') and ";
                        }
                        else 
                        {
                            SqlStr += item.Key + "='" + item.Value + "' and ";
                        }
                    }
                    SqlStr = SqlStr.Substring(0, SqlStr.Length - 4);
                }
            }
            else if (enum_dbType == SqlServer)
            {
                if (dic.Count != 0)
                {
                    SqlStr = " where  1 = 1 and ";
                    foreach (var item in dic)
                    {
                        if (item.Key == "begintime")
                        {
                            SqlStr += "RecordTime >='" + item.Value + "' and ";
                        }
                        else if (item.Key == "endtime")
                        {
                            SqlStr += "RecordTime <='" + item.Value + "' and ";
                        }
                        else
                        {
                            SqlStr += item.Key + "='" + item.Value + "' and ";
                        }
                    }
                    SqlStr = SqlStr.Substring(0, SqlStr.Length - 4);
                }
            }
            return SqlStr;
        }

 

二、对查询出来的数据分页

分页无非是从数据库里取出特定的条数。如:总数据有50条,现在前端要显示第三页数据,那么用sql语句从数据库中取出序号为第20~30条的数据即可。

那么主要的任务就是

1、计算好要从数据库查询数据的序号,要特别注意查询最后一页数据时的情况,因为最后一页显示的数据可能不足一页。

2、拼接sql语句,

这里有Sql Server和Oracle两种数据库查询语句的拼接。

Sql Server中有取top的方法,还有取row_num()的系统方法,取top的方法相对更简单。调用row_num()的话要单独为row_num()指定排序行。

Oracle中没有top,要用rownum这个系统提供的方法,相对来说也简单,只需在子查询后加rownum>x

/// <summary>
        /// 分页计算逻辑:如果取20~30条
        /// 首先取top30,倒排序,
        /// 再取top10,此时即20~30条数据
        /// </summary>
        /// <param name="PageShowNum">每页显示数量</param>
        /// <param name="clickpagenow">当前选择跳转页号</param>
        /// <param name="dtcount">总数据量</param>
        /// <param name="sql">查询条件</param>
        /// <returns></returns>
        public static string GetSqlWithTop(string PageShowNum, string clickpagenow, DataTable dtcount, string sql)
        {
            int top1 = 1;//第一次取范围(0 ~ X)
            int top2 = 1;//第二次取范围(Y ~ X)
            double everypage = Convert.ToDouble(PageShowNum);//每页显示数量
            double clickpage = Convert.ToDouble(clickpagenow);//当前选择跳转页号
            double allcount = Convert.ToDouble(dtcount.Rows[0][0]);//总数据量

            //跳转查询最后一页 或大于最后一页时
            if (allcount < everypage * clickpage)
            {
                //如果跳转页大于最后一页,修改跳转页为最后一页
                if (Math.Ceiling(Convert.ToDouble(allcount / everypage)) < clickpage)
                {
                    clickpage = (int)Math.Ceiling(Convert.ToDecimal(allcount / everypage));
                }
                //获取最后一页显示条数
                top1 = (int)(everypage - (everypage * clickpage - allcount));
                top2 = (int)allcount;
            }
            else
            {
                //中间页
                top1 = (int)(everypage);
                top2 = (int)(clickpage * everypage);
            }

            if (enum_dbType == Oracle)
            {
                //拼接Oracle
                return string.Format(@"
                                        select * from(
                                        select * from (
                                        select * from Log {0} ORDER BY RecordTime ASC)a 
                                        where rownum <={1} ORDER BY RecordTime DESC)b 
                                        where rownum<={2} 
                                        order by b.RecordTime asc", sql, top1, top2);
            }
            else if (enum_dbType == SqlServer)
            {
                //拼接SqlServer
                return string.Format(@"
                                        select * from(
                                        select top {0} * from (
                                        select top {1} * from Log {2}  
                                        order by RecordTime desc)a )b 
                                        order by RecordTime asc", top2, top1, sql);
            }
            else
                return "select top 10 * from Log";
        }

三、调用Sql

两次执行sql查询

第一次从数据库查出数据总数

第二次根据数据总数,计算好要显示的分页的数据,拼接成sql

其实更好的办法是将两个sql写在一起,或是写成存储过程,这样对数据更新较快的数据库,取值会更准确,查询数据库次数也只有一次

public List<Log> GetLog(Dictionary<string, string> Param)
        {
                DBConnection db = new DBConnection();
                //获取数据总数
                string sqlcount = "select count(*) from Log " + CommonMethod.SqlStrWithTime(Param);
                DataTable dtcount = db.QueryDT(sqlcount);


                //拼接子查询语句的查询条件
                string sql = SqlStrWithTime(Param);
                //拼接范围查询语句
                string lastsql = GetSqlWithTop(Param["PageShowNum"], Param["clickpagenow"], dtcount, sql);
                DataTable dt = db.QueryDT(lastsql);

                //赋值操作
                for (int i = 0; i < dt.Rows.Count; i++)
                {
                    Log obj = new Log();
                    obj.ID = dt.Rows[i]["ID"];
                    obj.UserName = dt.Rows[i]["UserName"];
                    obj.TaskID = dt.Rows[i]["TaskID"];
                    obj.Level = dt.Rows[i]["LogLevel"];
                    obj.RecordTime = (DateTime.Parse(dt.Rows[i]["RecordTime"].ToString())).ToString("yyyy-MM-dd HH:mm:ss");
                    obj.Info = dt.Rows[i]["Info"];
                    list.Add(obj);
                }
                return list;
        }