我们在页面展示一个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;
}