前言:上次代码打不开,今天换了一种代码展现发式。
在上篇一 次数据访问层的改进之路 中,我们经过改进后查询列表得到的编码结果类似下面:
1 | OleDbParameter[] paras = new ACEParameterHelper().AddParameter< string >( "@memoType" , memoType) |
3 | List<DataDictItemInfo> list = null ; |
4 | list = ACEAdvanceHelper.GetList<DataDictItemInfo>( "select * from Fk_data_dict_item t where t.memo=@memoType and t.parent_id=0" , conn, paras); |
代码还算简洁,能满足基本的查询要求!
但是实际应用中,这是不够的,这个组件主要还是要写sql语句的,写sql语句的一大繁琐事情就是写分页sql语句。
我们的问题就是:
1.分页方法选择:not in法和min-max法。
04 | select top 10 * from LandRightInfo where id not in (select top 10000 id from LandRightInfo) |
09 | select top 10 * from LandRightInfo where id > (select max(id) from (select top 10000 id from LandRightInfo)) |
11 | select top 10 * from LandRightInfo where id < (select min(id) from (select top 10000 id from LandRightInfo)) |
13 | select top 10 * from LandRightInfo where id > (select max(id) from (select top 10000 id from LandRightInfo order by id desc)) order by id desc |
比较一下,我采用了min-max法来分页。
2.分析min-max语句的特点
就以这个为例子:
1 | select top 10 * from LandRightInfo where id > (select max(id) from (select top 10000 id from LandRightInfo order by id desc)) order by id desc |
作用:查询第100页,第10001-10010条记录
10,代表的就是pageSize
10000,代表的就是当前页数之前页数不显示的数据列表项
提取一下:
1 | select top pageSize * from LandRightInfo where orderKey > (select max(orderKey) from (select top (pageIndex-1)*pageSize id from LandRightInfo)) |
这是个基本的模型。
第一个问题,经测试,这个模型有个问题,当第一页的时候,有问题,sql语句有错误。
所以,当pageIndex=1的时候,得单独写个模型,直接这样:
1 | select top pageSize * from LandRightInfo |
第二个问题,加排序条件,规则就是内查询和外查询(不包括min-max查询)的最后面都加上order by语句
1 | select top pageSize * from LandRightInfo where orderKey > (select max(orderKey) from (select top (pageIndex-1)*pageSize id from LandRightInfo order by id desc)) order by id desc |
第三个问题,加查询条件,加在order by语句前面,两个order by之前都要加
这里有个技巧,你直接加"where ..."的,就写的很死,不够灵活,咱们可以写成"where 1=1 ",再在后面Append条件"and ...",就可以多条件查询了。
代码就没什么贴的了。
3.如何动态生成这样的min-max语句
根据上面的分析和思路,我们也是很容易生成这样的语句的,方法如下:
01 | private string CreatePageSql() |
04 | StringBuilder result = new StringBuilder( "select top " ); |
05 | result.AppendFormat( "{0} * from {1} where " , pageSize, tableName); |
09 | if (orderKey != null ) |
11 | result.AppendFormat( "1=1 {0} order by {1} {2}" , condition, orderKey, orderType); |
17 | result.Append(orderKey); |
19 | int removePageNum = (pageIndex - 1) * pageSize; |
21 | if (orderType == "desc" ) |
23 | result.AppendFormat( "< (select min({0}) from (select top {1} {2} from {3} where 1=1 {4} order by {5} desc))" , orderKey, removePageNum, orderKey, tableName, condition, orderKey); |
24 | result.AppendFormat( " {0}" , condition); |
25 | result.AppendFormat( " order by {0} desc" , orderKey); |
29 | result.AppendFormat( "> (select max({0}) from (select top {1} {2} from {3} where 1=1 {4}))" , orderKey, removePageNum, orderKey, tableName, condition); |
30 | result.AppendFormat( " {0}" , condition); |
34 | return result.ToString(); |
从上面的方法可以看出,排序的字段OrderKey是必须的。
好了,生成sql的工具也有了。
4.如何以优雅的代码封装这个分页方法?
我相信很多人,都会写这样的封装代码:
02 | public List<T> GetList<T>( int pageIndex, int pageSize, string OrderKey,...) |
06 | string sql = CreatePageSql(); |
07 | list = ACEAdvanceHelper.GetList<T>(sql, conn, paras); |
自从见识了linq一起其他的一些ORM框架后,我突然觉得这种方法,代码看上去很一般,谈不上优雅,可读性并不强(个人观点)。
我觉得用一种类似函数式编程的方式给每个参数赋值更形象更美丽,可读性也更好,具体的我也不分析了,直接上类代码:
001 | public class ACEPageHelper |
006 | private int recordCounts = 0; |
010 | private int pageCounts = 0; |
012 | private string tableName; |
014 | private int pageSize = 0; |
015 | private int pageIndex = 0; |
017 | private string condition = "" ; |
019 | private string orderKey = null ; |
020 | private string orderType = null ; |
022 | private OleDbConnection conn = null ; |
023 | private OleDbParameter[] paras = null ; |
026 | public ACEPageHelper Connect(OleDbConnection conn) |
032 | public ACEPageHelper Parameter(OleDbParameter[] paras) |
037 | public ACEPageHelper TableName( string tableName) |
039 | this .tableName = tableName; |
042 | public ACEPageHelper PageSize( int pageSize) |
044 | this .pageSize = pageSize; |
048 | public ACEPageHelper PageIndex( int pageIndex) |
050 | this .pageIndex = pageIndex; |
054 | public ACEPageHelper Condition( string condition) |
056 | this .condition += " and " ; |
057 | this .condition += condition; |
061 | public ACEPageHelper OrderKey( string orderKey) |
063 | this .orderKey = orderKey; |
067 | public ACEPageHelper Asc() |
069 | this .orderType = "asc" ; |
073 | public ACEPageHelper Desc() |
075 | this .orderType = "desc" ; |
079 | public List<T> GetList<T>() |
081 | List<T> list = null ; |
083 | string sql = CreatePageSql(); |
084 | list = ACEAdvanceHelper.GetList<T>(sql, conn, paras); |
090 | private string CreatePageSql() |
093 | StringBuilder result = new StringBuilder( "select top " ); |
094 | result.AppendFormat( "{0} * from {1} where " , pageSize, tableName); |
098 | if (orderKey != null ) |
100 | result.AppendFormat( "1=1 {0} order by {1} {2}" , condition, orderKey, orderType); |
106 | result.Append(orderKey); |
108 | int removePageNum = (pageIndex - 1) * pageSize; |
110 | if (orderType == "desc" ) |
112 | result.AppendFormat( "< (select min({0}) from (select top {1} {2} from {3} where 1=1 {4} order by {5} desc))" , orderKey, removePageNum, orderKey, tableName, condition, orderKey); |
113 | result.AppendFormat( " {0}" , condition); |
114 | result.AppendFormat( " order by {0} desc" , orderKey); |
118 | result.AppendFormat( "> (select max({0}) from (select top {1} {2} from {3} where 1=1 {4}))" , orderKey, removePageNum, orderKey, tableName, condition); |
119 | result.AppendFormat( " {0}" , condition); |
123 | return result.ToString(); |
129 | public int GetCounts() |
131 | StringBuilder sql = new StringBuilder( "select " ); |
132 | sql.AppendFormat( "count({0})" , orderKey); |
133 | sql.AppendFormat( " from {0} where 1=1 " , tableName); |
134 | sql.Append(condition); |
136 | int result = Convert.ToInt32(ACECommonHelper.ExcuteScalar(sql.ToString(), conn, paras)); |
138 | this .recordCounts = result; |
145 | /// <returns></returns> |
146 | public int GetPages() |
150 | if (recordCounts == 0) |
155 | if (recordCounts % pageSize == 0) |
157 | result = recordCounts / pageSize; |
161 | result = (recordCounts / pageSize) + 1; |
怎么个优雅法,接着往下看。
5.使用上面的类以及效率测试
基本使用(最后一页):
01 | using (OleDbConnection conn = new OleDbConnection( "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=|DataDirectory|AccessHelper_390602.mdb;" )) |
03 | List<LandRightInfo> list = null ; |
04 | ACEPageHelper pageData= new ACEPageHelper(); |
05 | list = pageData.Connect(conn) |
06 | .TableName( "LandRightInfo" ) |
10 | .GetList<LandRightInfo>(); |
11 | Response.Write( "总结果数/页数:" + pageData.GetCounts() + "条/" +pageData.GetPages()+ "页<br/>" ); |
加个条件:
1 | list = pageData.Connect(conn) |
2 | .TableName( "LandRightInfo" ) |
5 | .OrderKey( "LandCode" ) |
6 | .Condition( "LandCode like '%5%'" ) |
7 | .Condition( "id like '%5%'" ) |
9 | .GetList<LandRightInfo>(); |
一目了然啊,每页多少条,第几页,查询条件是什么,升序还是降序,这代码看上去就带劲啊,呵呵。
参数化查询:
02 | OleDbParameter[] paras = new ACEParameterHelper() |
03 | .AddParameter< int >( "@id" , 1000) |
05 | list = pageData.Connect(conn) |
06 | .TableName( "LandRightInfo" ) |
11 | .Condition( "LandCode like '%5%'" ) |
12 | .Condition( "id <@id" ) |
13 | .GetList<LandRightInfo>(); |
注:max,min函数也可应用于文本字段,所以这里的OrderKey也可以是文本字段,效率还行
目前,这种方法只能处理单表,夺标复杂的查询还不行,但这是一个好的开始,而且这个函数的思想也是可以应用到其他数据库的分页查询中去,我觉得这篇 博客的实用性还是挺强的,个人推荐一下。
完美了,查询出来的速度也是飞快飞快的。
查询语句:
01 | list = pageData.Connect(conn) |
02 | .TableName( "LandRightInfo" ) |
06 | .GetList<LandRightInfo>(); |
08 | {select top 10 * from LandRightInfo where 1=1 order by id } |
10 | {select top 10 * from LandRightInfo where id> (select max(id) from (select top 10 id from LandRightInfo where 1=1 )) } |
12 | {select top 10 * from LandRightInfo where id> (select max(id) from (select top 360 id from LandRightInfo where 1=1 and LandCode like '%5%' and id <@id)) and LandCode like '%5%' and id <@id} |
14 | {select top 50 * from LandRightInfo where LandCode< (select min(LandCode) from (select top 50 LandCode from LandRightInfo where 1=1 and LandCode like '%5%' and id like '%5%' order by LandCode desc)) and LandCode like '%5%' and id like '%5%' order by LandCode desc} |
---------------39万数据测试第一页:=3.5s-----------------
总结果数/页数:390602条 /39061页
起始时间:1:34:41
结束时间:1:34:45
耗时Ticks:35462029
---------------39万数据测试第2页:<1s-----------------
总结果数/页数:390602条 /39061页
起始时间:1:32:54
结束时间:1:32:54
耗时Ticks:630036
---------------39万数据测试第30000页:<1s-----------------
总结果数/页 数:390602条/39061页
起始时间:1:44:19
结束时间:1:44:20
耗时Ticks:6630379
希望大家多提宝贵意见,多多支持!