关于PDF.NET开发框架的名字由来
在设计www.pwmis.com站点的时候,考虑到架构的兼容性和将来升级的可能性,最重要的是没有足够的时间去为网站添加和维护很多复杂的程序,所以在借鉴前人成功经验的基础上,设计了一套全新的快速数据处理框架 PWMIS Data Develop Framework,简称PDF。
本套框架的思想是借鉴Java平台的Hibernate 和 iBatis 而来,兼有ORM和SQL-MAP的特性,同时还参考了后来.NET的LINQ(本框架成型于2006年,当时还未听说过LINQ)使用风格,设计了OQL查询表达式。本框架的设计思想是通用的,完全可以移植到Java 平台,现在只提供了.NET平台的实现,暂且将本框架命名为 PDF.NET(当前测试版 4.6.4.0528 )
关于我与此框架的关系
偶尔得知(看MOON.Orm对pdf cyq ClownFish等框架进行性能对比)这套框架不错,便想看看到底如何。从百度搜索,搜到官网,并下载阅读之。因为之前反编译了Moon.ORM,并把修改后的部分核心代码交给了原作者,就算促进中国软件的进步吧,一直想搞一套很厉害的底层,所以拜读之,写此文,并无任何攻击的恶意,因为我不喜欢那些天天喷的家伙,只为探讨技术并获取pdf svn账号获取最新代码而已。
首先我们先看查询细节,以MySql为例
-----------------------------------------------------------------------------------------
使用Offset方式
-----------------------------------------------------------------------------------------
说明了什么呢?即offset是把数据记录从0开始索引(0条,1条,2条.......)
先把PDF.NET原代码附上:
private static string MakePageSQLStringByMySQL_PgSQL(string strSQLInfo, string strWhere, int PageSize, int PageNumber, int AllCount, string offsetString) { strSQLInfo = strSQLInfo.Trim(); //去除末尾的分号 if (strSQLInfo.EndsWith(";")) strSQLInfo = strSQLInfo.TrimEnd(';'); if (strWhere != null && strWhere != "") { strWhere = strWhere.Trim().ToUpper(); if (strWhere.StartsWith("WHERE ")) throw new Exception("附加查询条件不能带 where 谓词"); if (strWhere.IndexOf(" ORDER BY ") > 0) throw new Exception("附加查询条件不能带 ORDER BY 谓词"); strSQLInfo = "SELECT * FROM (" + strSQLInfo + ") temptable0 WHERE " + strWhere; } if (AllCount == 0) { //生成统计语句 return "select count(*) from (" + strSQLInfo + ") "; } if (PageNumber == 1) return strSQLInfo + " LIMIT " + PageSize; int offset = PageSize * PageNumber; if (offsetString == ",")//MySQL,感谢网友[左眼]发现此Bug return strSQLInfo + " LIMIT " + offset + offsetString + PageSize; else //PostgreSQL return strSQLInfo + " LIMIT " + PageSize + offsetString + offset; }
1.用pdf框架开发Mysql Sqlite PostgreSQL数据库的朋友在进行分页时要小心了,(PageNumber为2,即会查询出第3页的数据,依此类推......)。
2.用+号来进行字符串拼接,本人还是觉得StringBuilder要好,因为传入的strSQLInfo可能大量sql语句。效率.......
进过修改后的代码为:
1 private static string MakePageSQLStringByMySQL_PgSQL1(string strSQLInfo, string strWhere, int PageSize, int PageNumber, int AllCount, string offsetString) 2 { 3 strSQLInfo = strSQLInfo.Trim(); 4 //去除末尾的分号 5 if (strSQLInfo.EndsWith(";")) 6 strSQLInfo = strSQLInfo.TrimEnd(';'); 7 8 StringBuilder sb = new StringBuilder(); 9 if (string.IsNullOrEmpty(strWhere)) 10 { 11 sb.Append(strSQLInfo); 12 } 13 else 14 { 15 strWhere = strWhere.TrimStart(); 16 if (strWhere.StartsWith("WHERE ", StringComparison.OrdinalIgnoreCase)) 17 throw new Exception("附加查询条件不能带 where 谓词"); 18 if (strWhere.LastIndexOf(" ORDER BY ", StringComparison.OrdinalIgnoreCase) > 0) 19 throw new Exception("附加查询条件不能带 ORDER BY 谓词"); 20 21 sb.Append("SELECT * FROM (") 22 .Append(strSQLInfo) 23 .Append(") temptable0 WHERE ") 24 .Append(strWhere); 25 26 //sb.AppendFormat("SELECT * FROM ({0}) temptable0 WHERE {1}", strSQLInfo, strWhere); 27 } 28 29 if (AllCount == 0) 30 { 31 //生成统计语句 32 sb.Insert(0, "select count(*) from (").Append(") "); 33 } 34 else 35 { 36 int offset = PageSize * (PageNumber - 1);//应该是 PageSize * (PageNumber -1) 而不是 PageSize * PageNumber
37 sb.AppendFormat(" LIMIT {0} OFFSET {1}", PageSize, offset); 38 } 39 return sb.ToString(); 40 }
最终测试代码:
1 //这么做是因为是查询的语句在实际环境下几乎每次不同,而字符串类的实现方式用了享元模式(同样的字符串内容,在内存中只有一个实例),所以我们用不同的 String实例 2 string strInfo = @"SELECT SelectField{0},SelectField{1},SelectField2,SelectField3,SelectField4,SelectField5,SelectField6,SelectField7 FROM 3 FROM TABLE{0} 4 ORDER BY OrderField1 DESC,OrderField2 ASC"; 5 string where = " 1=1 and (name='张三' and age=20 and sex=0) or(game='过家家' and num={0} and num1={1}) "; 6 List<string> infoList = new List<string>(); 7 List<string> whereList = new List<string>(); 8 //准备测试数据 9 for (int i = 0; i < 500000; i++) 10 { 11 infoList.Add(string.Format(strInfo, i, i + 1)); 12 whereList.Add(string.Format(where, i, i + 1)); 13 } 14 //string page 15 Stopwatch sw = new Stopwatch(); 16 sw.Start(); 17 for (int i = 1; i < 500000; i++) 18 { 19 MakePageSQLStringByMySQL_PgSQL(infoList[i], whereList[i], 20, i, 10000, string.Empty); 20 } 21 sw.Stop(); 22 double stringPageTime = sw.Elapsed.TotalMilliseconds / 1000; 23 //string count 24 sw.Restart(); 25 for (int i = 1; i < 500000; i++) 26 { 27 MakePageSQLStringByMySQL_PgSQL(infoList[i], whereList[i], 20, i, 0, string.Empty); 28 } 29 sw.Stop(); 30 double stringCountTime = sw.Elapsed.TotalMilliseconds / 1000; 31 32 //StringBuilder page 33 sw.Restart(); 34 for (int i = 1; i < 500000; i++) 35 { 36 MakePageSQLStringByMySQL_PgSQL1(infoList[i], whereList[i], 20, i, 10000, string.Empty); 37 } 38 sw.Stop(); 39 double stringBuilderTime = sw.Elapsed.TotalMilliseconds / 1000; 40 //StringBuilder count 41 sw.Restart(); 42 for (int i = 1; i < 500000; i++) 43 { 44 MakePageSQLStringByMySQL_PgSQL1(infoList[i], whereList[i], 20, i, 0, string.Empty); 45 } 46 sw.Stop(); 47 double stringBuilderCountTime = sw.Elapsed.TotalMilliseconds / 1000; 48 49 MessageBox.Show("构造分页语句: " + stringPageTime.ToString() + " 秒 构造count: " + stringCountTime.ToString() + " 秒\n" 51 + "构造分页语句1:" + stringBuilderTime.ToString() + " 秒 构造count1:" + stringBuilderCountTime.ToString() + " 秒");
结果附上,大家可以自己测下: