本文所要讲的是在配餐系统开发中,所使用的分页方法和当时的思考——实际上这个配餐系统软件在今年2月份已经做完,这篇博客也本应该早写出来了,但因为各种原因:之前忙和之后的状态不佳(主要还是个人原因,如果想写,时间不挤也是有的,懒散了),导致此文一直迟迟未写。转到正题,说到分页——最直接或普遍的,我们需要考虑的是分页SQL的写法,像select top .... id(表主键)> 或 not in...,这样的写法算是我们最常见和使用的(像学习编程或项目经验很少时),而其中 id(表主键)> 比 not in 的效率要高,但当数据量比较大时(几万或几十万条的信息),查询速度就会比较明显的降低(我这里特制的是access数据库下,对于其它高级或性能优化比较好的数据库mysql,sqlserver等数据库下可能影响比较小)。我做的配餐系统这个项目,使用的是access数据库,最开始采用的id(表主键)>的分页方法,在导入近万条的食物数据后,查询速度就明显的下降,导致界面假死,这样即使在数据不增加的情况下,用户体验性也不好;问题必须要解决,于是考虑改进和优化——需要找一个更高效的分页算法。
(软件中的界面图)
看上图,现在想要查出最后一页的记录,你会怎么写查询SQL? 如果按上面的的两种方式,就必须先查询出n-1页的记录或其中最大的主键id,但我如果直接查询最后一页的记录——select top 最后一页的记录数(根据总记录数和pageSize可以得出),哪种方式速度更快,想必你很容易能看出来!(其实,我们可以将查询看作是寻找或走路,从最后一条记录查询最后一页的记录当然更快), 于是,上图对应到分页,就可以得出,如果是查询页<=中间页的,从第一页开始查询更快,而查询页>中间页的,从最后一页开始查询更快。说的比较模糊,直接贴出代码如下:
1 #region 分页查询的SQL
2
3
4 ///<summary>
5 /// 返回 分页查询的SQL
6 ///</summary>
7 ///<param name="pageSize">每页要显示的记录的数目</param>
8 ///<param name="pageCount">页面的总数</param>
9 ///<param name="pageIndex">要显示的页的索引</param>
10 ///<param name="recordCount">当前要分页的记录总数</param>
11 ///<param name="tableName">要查询的数据表</param>
12 ///<param name="queryFields">要查询的字段</param>
13 ///<param name="primaryKey">主键字段</param>
14 ///<param name="ascending">是否为升序排列</param>
15 ///<param name="condition">查询的筛选条件</param>
16 ///<param name="order">查询的排序条件,一般按 主键 排序</param>
17 ///<returns></returns>
18 public static string PageView_Com(int pageSize,int pageCount,int pageIndex,int recordCount,string tableName,string queryFields,string primaryKey,bool ascending,string condition,string order)
19 {
20 if (string.IsNullOrEmpty(condition))
21 condition = " 1=1 ";
22 //if (string.IsNullOrEmpty(order))
23 // order = PrimaryKey;
24
25 int firstIndex = 0; //第一页的索引
26 if (pageIndex <= firstIndex)
27 return String.Format("SELECT top {0} {1} FROM {2} where {3} order by {4} {5} ", pageSize, queryFields, tableName, condition, primaryKey, GetSortType(ascending));
28
29 StringBuilder sql = new StringBuilder();
30 int middleIndex = GetMidPageIndex(pageCount); //中间页的索引
31 int lastIndex = pageCount - 1; //最后一页的索引
32
33 if (pageIndex > firstIndex && pageIndex <= middleIndex)
34 {
35 #region 前半部分查询
36 sql.Append("SELECT TOP ").Append(pageSize).Append("")
37 .Append(queryFields).Append(" FROM ").Append(tableName)
38 .Append(" WHERE ").Append(primaryKey);
39 if (ascending)
40 sql.Append(" > (").Append(" SELECT MAX(");
41 else
42 sql.Append(" < (").Append(" SELECT MIN(");
43 sql.Append(primaryKey).Append(") FROM ( SELECT TOP ")
44 .Append(pageSize * pageIndex).Append("").Append(primaryKey)
45 .Append(" FROM ").Append(tableName);
46 if (condition != String.Empty)
47 sql.Append(" WHERE ").Append(condition);
48 sql.Append(" ORDER BY ").Append(primaryKey).Append("")
49 .Append(GetSortType(ascending)).Append(" ) TableA )");
50 if (condition != String.Empty)
51 sql.Append(" AND ").Append(condition);
52 sql.Append(" ORDER BY ").Append(primaryKey).Append("")
53 .Append(GetSortType(ascending));
54 #endregion
55 }
56 else if (pageIndex > middleIndex && pageIndex < lastIndex)
57 {
58 #region 后半部分查询
59 sql.Append("SELECT * FROM (").Append("SELECT TOP ").Append(pageSize)
60 .Append("").Append(queryFields).Append(" FROM ").Append(tableName)
61 .Append(" WHERE ").Append(primaryKey);
62 if (ascending)
63 sql.Append(" < (").Append(" SELECT MIN(");
64 else
65 sql.Append(" > (").Append(" SELECT MAX(");
66 sql.Append(primaryKey).Append(") FROM ( SELECT TOP ")
67 .Append(recordCount - pageSize * (pageIndex + 1)).Append("")
68 .Append(primaryKey).Append(" FROM ").Append(tableName);
69 if (condition != String.Empty)
70 sql.Append(" WHERE ").Append(condition);
71 sql.Append(" ORDER BY ").Append(primaryKey).Append("")
72 .Append(GetSortType(!ascending)).Append(" ) TableA ) ");
73 if (condition != String.Empty)
74 sql.Append(" and ").Append(condition);
75 sql.Append(" ORDER BY ").Append(primaryKey).Append("").Append(GetSortType(!ascending));
76 sql.Append(" ) TableB ORDER BY ").Append(primaryKey).Append("").Append(GetSortType(ascending));
77 #endregion
78 }
79 else if (pageIndex >= lastIndex)
80 {
81 #region 最后一页
82 sql.Append("SELECT * FROM ( SELECT TOP ").Append(recordCount - pageSize * lastIndex)
83 .Append("").Append(queryFields).Append(" FROM ").Append(tableName);
84 if (condition != String.Empty)
85 sql.Append(" WHERE ").Append(condition);
86 sql.Append(" ORDER BY ").Append(primaryKey).Append("").Append(GetSortType(!ascending));
87 sql.Append(") as TableA ORDER BY ").Append(primaryKey).Append("").Append(GetSortType(ascending));
88 #endregion
89 }
90 return sql.ToString();
91 }
92
93 ///<summary>
94 /// 计算中间页的页索引
95 ///</summary>
96 ///<param name="pageCount"></param>
97 ///<returns></returns>
98 private static int GetMidPageIndex(int pageCount)
99 {
100 return (int)Math.Ceiling((double)pageCount / 2) - 1;
101 }
102 ///<summary>
103 /// 获取排序的方式("ASC"表示升序,"DESC"表示降序)
104 ///</summary>
105 ///<param name="ascending"></param>
106 ///<returns></returns>
107 private static String GetSortType(bool ascending)
108 {
109 return (ascending ? "ASC" : "DESC");
110 }
111 #endregion
看了代码,你会发现其实其中的分页查询并没有用到什么其它的数据库函数或新的方法,变的只是查询的策略——想法。
好了,就此结尾。希望此文或文中的考虑问题的想法能对你有所帮助,也希望大家能多提意见和分享你的快速查询方面的经验!