如何用c lambda 表达式表达式 写任意条件组合的搜索语句

玩蛇网提供最新Python编程技术信息以及Python资源下载!
您现在的位置:
玩蛇网推荐图文教程:
本文将就python lambda表达式,在Python中作用及用法做个简单的介绍,要想深入学习了解lambda的妙用,还需要在更多实践作业中积累经验,加入自己的理解才能更好掌握。
python lambda概念
在Pythony语言中除了def语句用来定义之外,还可以使用匿名函数Lambda,它是Python一种生成函数对象的表达式形式。匿名函数通常是创建了可以被调用的函数,它返回了函数,而并没有将这个函数命名。lambda有时被叫做匿名函数也就是这个原因,需要一个函数,又不想动脑筋去想名字,这就是匿名函数。
def func(a,b,c):
return a+b+c
print func(1,2,3)
f = lambda a,b,c:a+b+c
print f(1,2,3)
python lambda作用
Lambda是一个表达式,而并非语句
因为Lambda是一个表达式,所以在python语言中可以出现在def语句所不能出现的位置上;
Lambda与def语句相比较,后者必须在一开始就要将新函数命名;而前者返回一个新函数,可以有选择性的。
Lambda主体只是单个表达式,并而一个代码块
lambda与def比较它的功能更小,它只是一个为简单函数所服务的对象,而def能处理更大型的任务。
为什么要使用lambda?
用python学习手册中的一段话来回答这个问题非常好&lambda有起到速写函数的作用,允许在使用的代码内嵌入一个函数的定义。在仅需要嵌入一小段可执行代码的情况 下,就可以带来更简洁的代码结构。&lambda的使用在知识学习中就会遇到,但真正应用时还是在的阶段,这时需要你做更深入学习。
lambda python 总结
以上只是玩蛇网python学习与分享平台,为大家对Python Lambda表达式的作用做了一个简单的介绍,它的使用技巧和注意事项还有很多。但不要把lambda想象的很可怕,在python中你只要把它当做是个关键词,一个用来引入表达式的语法而已。除去以上的不同之处,def和lambda可以完相同的工作,它也比你想象中更容易使用。
玩蛇网文章,转载请注明出处和来源网址:
微信公众号搜索"玩蛇网Python之家"加关注,每日最新的Python资讯、图文视频教程可以让你一手全掌握。强烈推荐关注!
【微信扫描下图可直接关注】
玩蛇网PythonQQ群,欢迎加入: ①
出炉日期: 15:43
我要分享到:
评论列表(网友评论仅供网友表达个人看法,并不表明本站同意其观点或证实其描述)
必知PYTHON教程
Must Know PYTHON Tutorials
必知PYTHON模块
Must Know PYTHON Modules
Navigation
玩蛇网Python之家,简称玩蛇网,是一个致力于推广python编程技术、程序源码资源的个人网站。站长 斯巴达 是一位
长期关注 软件、互联网、服务器与各种开发技术的Python爱好者,建立本站旨在与更多朋友分享派森编程的乐趣!
本站团队成员:
欢迎加入团队...& EntityFramework动态组合Lambda表达式作为数据筛选条件,代替拼接SQL语句
EntityFramework动态组合Lambda表达式作为数据筛选条件,代替拼接SQL语句
传统的操作数据库方式,筛选数据需要用StringBuilder拼接一大堆的WHERE子句。
在Entity Framework中,代码稍有不慎就会造成巨大性能消耗,如:
using(var db=new MyDbContext())
var s= db.Students.ToList().First(s=&s.ID=1200);
嘣!进行了全表数据读取!当然一般人也不会犯这种低级的错误,言归正传。
可以简单的这样筛选数据:
using(var db=new MyDbContext())
var list =db.Students.AsQueryable();
if(********){list=list.Where(s=&s.ID=1200);}
if(******){list=list.Where(...)}
但是有时这种方法不能完成特定需求,如:
using(var db=new MyDbContext())
var list =db.Students.AsQueryable();
if(条件1){list=list.Where(s=&s.ID&1200);}
if(条件2){list=list.Where(s=&s.ID&1000);}
现在条件1和条件2同时成立,得到的是空结果集而不是ID&1200和ID&1000的结果集。
这只是两个并列简单条件的组合,如果是条件嵌套呢?
下面是假想:
using (var db = new MyDbContext())
Expression&Func&Student, bool&& checkStudent1 = s1 =& s1.ID & 1200;
Expression&Func&Student, bool&& checkStudent2 = s2 =& s2.ID & 1000;
Expression.Lambda&Func&Student, bool&&(
Expression.Or(checkStudent1.Body, checkStudent2.Body), checkStudent1.Parameters);
var result = db.Students.Where(e).ToList();
叫它假想的原因是执行会产生异常”The parameter 's2' was not bound in the specified LINQ to Entities query expression“。
e的内容是{s1 =& ((s1.ID & 1200) Or (s2.ID & 1000))},很明显s2这个参数是没有被定义的。
实际上我们一直操作一个Student表,最终我们想要的也是多Lambda表达式合在一起对该Student表的操作。换句话说,s2应该用s1代替。
有人说了,这样:
Expression&Func&Student, bool&& checkStudent1 = s =& s.ID & 1200;
Expression&Func&Student, bool&& checkStudent2 = s =& s.ID & 1000;
Expression.Lambda&Func&Student, bool&&(
Expression.Or(checkStudent1.Body, checkStudent2.Body), checkStudent1.Parameters);
var result = db.Students.Where(e).ToList();
异常:”The parameter 's' was not bound in the specified LINQ to Entities query expression“。
e的内容是{s =& ((s.ID & 1200) Or (s.ID & 1000))},现在参数都一样是s了,但其实它们的GUID不同,也就是说它们还是两个不同的参数。
我们需要做的是手工把checkStudent2.Body里面的参数s换成checkStudent1.Body里面的参数s。
ExpressionVisitor可以很好的完成这步操作。拿个别人现成的例子来用:
[csharp] view plaincopy
public class ParameterRebinder : ExpressionVisitor
private readonly Dictionary&ParameterExpression, ParameterExpression&
public ParameterRebinder(Dictionary&ParameterExpression, ParameterExpression& map)
this.map = map ?? new Dictionary&ParameterExpression, ParameterExpression&();
public static Expression ReplaceParameters(Dictionary&ParameterExpression, ParameterExpression& map, Expression exp)
return new ParameterRebinder(map).Visit(exp);
protected override Expression VisitParameter(ParameterExpression p)
ParameterEx
if (map.TryGetValue(p, out replacement))
return base.VisitParameter(p);
更改后的测试代码:
Expression&Func&Student, bool&& checkStudent1 = s =& s.ID & 1200;
Expression&Func&Student, bool&& checkStudent2 = s =& s.ID & 1000;
var body2 =
ParameterRebinder.ReplaceParameters(
checkStudent2.Parameters.Select((s,i)=&new{s,f=checkStudent1.Parameters[i]}).ToDictionary(p=&p.s,p=&p.f), checkStudent2.Body);
Expression.Lambda&Func&Student, bool&&(
Expression.Or(checkStudent1.Body, body2), checkStudent1.Parameters);
var result = db.Students.Where(e).ToList();
至此表达式顺利拼接完成。当然这样使用还是麻烦,借用别人的扩展类稍微修改一下:
[csharp] view plaincopy
public static class PredicateBuilder
public static Expression&Func&T, bool&& True&T&() { return f =& }
public static Expression&Func&T, bool&& False&T&() { return f =& }
public static Expression&T& Compose&T&(this Expression&T& first, Expression&T& second, Func&Expression, Expression, Expression& merge)
// build parameter map (from parameters of second to parameters of first)
var map = first.Parameters.Select((f, i) =& new { f, s = second.Parameters[i] }).ToDictionary(p =& p.s, p =& p.f);
// replace parameters in the second lambda expression with parameters from the first
var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body);
// apply composition of lambda expression bodies to parameters from the first expression
return Expression.Lambda&T&(merge(first.Body, secondBody), first.Parameters);
public static Expression&Func&T, bool&& And&T&(this Expression&Func&T, bool&& first, Expression&Func&T, bool&& second)
pose(second, Expression.And);
public static Expression&Func&T, bool&& Or&T&(this Expression&Func&T, bool&& first, Expression&Func&T, bool&& second)
pose(second, Expression.Or);
参考:/b/meek/archive//linq-to-entities-combining-predicates.aspx
完美的动态拼接Lambda表达式如下:
using (var db = new MyDbContext())
var predicate = PredicateBuilder.True&Student&();
predicate=predicate.And(s =& s.ID & 1200);
predicate=predicate.Or(s =& s.ID & 1000);
var result = db.Students.Where(predicate).ToList();
下面是一个我自己使用的例子,仅供参考:
[csharp] view plaincopy
using (var db = new SHTrackerDbContext())
var predicate = PredicateBuilder.True&Course&();
settings = DecorateSettings(settings);
Expression&Func&Course, bool&& checkCourse = c =& db.Students.Any(s =& s.CourseID == c.ID);
if (!string.IsNullOrEmpty(settings.Quater_Year))
checkCourse =
c =& db.Students.Any(s =& s.CourseID == c.ID && db.Student2CBOs.Any(
s2c =& s2c.StudentID == s.ID && s2c.Quater_Year.Equals(settings.Quater_Year)));
if (settings.QuaterYearArray != null)
checkCourse =
c =& db.Students.Any(s =& s.CourseID == c.ID && db.Student2CBOs.Any(
s2c.StudentID == s.ID && settings.QuaterYearArray.Any(qy =& qy.Equals(s2c.Quater_Year))));
if (!string.IsNullOrEmpty(settings.DPU_ID))
checkCourse =
checkCourse.And(
c =& db.Students.Any(s =& s.CourseID == c.ID && s.DPU_ID.Equals(settings.DPU_ID)));
predicate = predicate.And(checkCourse);
if (settings.IsCheckInstructorName)
predicate = predicate.And(c =& c.InstructorName.Equals(settings.InstructorName));
if (!string.IsNullOrEmpty(settings.Term))
predicate = predicate.And(c =& c.TermDescription.Equals(settings.Term));
if (settings.TermArray != null)
predicate = predicate.And(c =& settings.TermArray.Any(t =& t.Equals(c.TermDescription)));
if (settings.CourseType != CourseType.All)
predicate = predicate.And(c =& c.Type == (int) settings.CourseType);
new CourseCollection(
db.Courses.AsNoTracking()
.Where(predicate)
.OrderByDescending(m =& m.ID)
.Skip((pageIndex - 1)*pageSize)
.Take(pageSize)
.ToList(),
db.Courses.AsNoTracking().Where(predicate).Count())
PageIndex = pageIndex,
PageSize = pageSize,
Settings = DecorateSettings(settings)
本文固定链接:
[上一篇][下一篇]
最新文章随机精彩热门排行
日志总数:3891 篇
评论总数:26840 评
标签数量:4468 个
链接总数:4 条
建站日期:
运行天数:1129 天ORM查询语言(OQL)简介–高级篇:脱胎换骨 – 深蓝医生 | 查问题
汇聚最新编程技术,编程问题一网打尽
& ORM查询语言(OQL)简介–高级篇:脱胎换骨 – 深蓝医生
ORM查询语言(OQL)简介–高级篇:脱胎换骨 – 深蓝医生
[ 分类: ]
&&& 在写本文之前,一直在想文章的标题应怎么取。在写了《》、《》之后,觉得本篇文章应该是前2篇的延续,但又不是一般的延续,因为今天要写的这篇内容,是基于对框架OQL完全重构之后来写的,所以加上一个副标题:脱胎换骨!
一、OQL之前生
1.1,内容回顾:
OQL是我设计用来处理PDF.NET开发框架的ORM查询的,因此叫做ORM查询语言。自2006年第一版以来,经历了多次重构,到PDF.NET Ver 4.X 版本,已经比较稳定了,在我做的项目和框架用户朋友的项目中得到成功应用,基本符合一般的常规应用需求。
OQL有下面3个显著特点:
抽象的SQL,屏蔽了具体数据库的差异,因此支持所有数据库;
对象化的&SQL&,写OQL代码能够获得IDE的智能提示;
没有使用.NET的特性,比如泛型、反射、表达式树等东西,因此理论上OQL可以跨语言平台,比如移植到Java,C++,VB等。
OQL的原理基于2大特性:
表达式的链式调用&&
属性的实例调用
OQL支持4大类数据操作
数据查询:
单实体类(单表)查询
多实体类(多表)关联查询
统计、聚合运算
1.2,老版本的局限
尽管OQL已经可以解决80%的查询需求,剩下的20%查询需求我都建议框架用户使用SQL-MAP技术来完成,但对于用户而言,是不太愿意从ORM模式切换到SQL模式的,希望OQL能够解决竟可能多的查询需求。那么,PDF.NET Ver 4.X 版本的OQL有哪些不足呢?
1.2.1,自连接查询
也成为内连接查询。OQL支持多表(实体)查询的,但却无法支持内连接查询,原因是内连接查询必须指定表的别名:
SELECT R1.readerid,R1.readername,R1.unit,R1.bookcountFROM ReaderInfo AS R1,ReaderInfo AS R2WHERE R2.readerid=9704AND R1.bookcount&R2.bookcount
—连接关系ORDER BY R1.bookcount
上面这个查询是从ReaderInfo表查询可借图书数目比编号为9704读者多的所有读者信息,这里对表使用了别名来实现的,如果不使用别名,那么这个查询就无法实现。而OQL之前的版本,是不支持表的别名的,因此,对于连接查询,OQL生成的可能是这样子的SQL语句:
SELECT teacher.*,student.* FROM teacher
INNER JOIN student
ON teacher.id=student.tea_id
1.2.2,子查询
老版本OQL仅支持IN条件的子查询,不能像SQL那么灵活的进行各种子查询,其实不支持的原因其中一个也是因为OQL查询不支持表的别名,另外是子查询无法获取到父查询的表名和字段名。子查询是一个很常用的功能,如果不能够支持,那么就大大限制了OQL的使用范围。
下面是来自SQLSERVER 联机帮助的说明:
子查询也称为内部查询或内部选择,而包含子查询的语句也称为外部查询或外部选择。
许多包含子查询的 Transact-SQL 语句都可以改用联接表示。其他问题只能通过子查询提出。在 Transact-SQL 中,包含子查询的语句和语义上等效的不包含子查询的语句在性能上通常没有差别。但是,在一些必须检查存在性的情况中,使用联接会产生更好的性能。否则,为确保消除重复值,必须为外部查询的每个结果都处理嵌套查询。所以在这些情况下,联接方式会产生更好的效果。以下示例显示了返回相同结果集的 SELECT 子查询和 SELECT 联接:
/* SELECT statement built using a subquery. */SELECT NameFROM AdventureWorks2008R2.Production.ProductWHERE ListPrice =
(SELECT ListPrice
FROM AdventureWorks2008R2.Production.Product
WHERE Name = ‘Chainring Bolts‘ );
/* SELECT statement built using a join that returns
the same result set. */SELECT Prd1. NameFROM AdventureWorks2008R2.Production.Product AS Prd1
JOIN AdventureWorks2008R2.Production.Product AS Prd2
ON (Prd1.ListPrice = Prd2.ListPrice)WHERE Prd2. Name = ‘Chainring Bolts‘;
1.2.3,OQL数据插入
尽管OQL可以支持实体类的批量更新与删除,但没有支持实体类的插入,原因是对单个实体类而言,可以直接调用EntityQuery&T&的Insert()方法实现。但项目中可能还是有需要写SQL插入数据的情况,比如插入Int类型的值为0,如果用实体类的方式那么该列不会被插入,因为PDF.NET的实体类认为该属性没有改变,PDF.NET的插入和更新操作,都只处理&属性值改变过的&数据。另外,也不支持Inser….Select….From 这种批量插入方式。
INSERT INTO MySalesReason
SELECT SalesReasonID, Name, ModifiedDate
FROM AdventureWorks2008R2.Sales.SalesReason
WHERE ReasonType = N‘Marketing‘;
&1.2.4,HAVING 子句
OQL老版本不支持该功能,尽管不是很常用,但配合分组查询还是有用的,如下面的例子HAVING 子句从 SalesOrderDetail 表中检索超过 $ 的每个 SalesOrderID 的总计。
SELECT SalesOrderID, SUM(LineTotal) AS SubTotalFROM Sales.SalesOrderDetailGROUP BY SalesOrderIDHAVING SUM(LineTotal) & ORDER BY SalesOrderID ;
&二、浴火重生
现在流行的ORM框架很多,刚刚在写本文的时候,还发现有个朋友写了一篇各ORM对比测试的文章:
&&这么多ORM框架,我并不是很熟悉,PDF.NET的目标只想在某些方面赶超MS的EF框架,据说现在EF6都快出来了,EF4.5在性能上上了一个台阶。面对EF这个强敌,如果PDF.NET不能解决前面说的几大缺陷,注定距离会越来越远,PDF.NET的用户对我也是常常提出批评,纷纷转投EF去了,对此我深感压力山大!
尽管EF是PDF.NET ORM 的强劲对手,但 PDF.NET ORM的查询语言OQL,相对于EF的查询语言Linq,还是有自己独立的特色,OQL比Linq更接近SQL,Linq是VS的语法糖,本质上VS编译器会将它转化成Lambda表达式,进一步转换成表达式树,最后翻译成SQL语句交给数据库去执行。所以我们会看到针对集合操作的扩展方法,有很多都要使用 =& 的调用方式,而OQL没有使用Lambda,它是怎么获取到查询对应的表名称和字段名称的呢?它是怎么实现SQL查询的层次结构的呢?
2.1,属性字段的秘密
PDF.NET属性的定义采用下面的形似:
public System.String UserName
get { return getProperty&System.String&(“UserName“); }
set { setProperty(“UserName“, value, 50); }
因此在获取实体类的属性值得时候,调用了getProperty&T&(“字段名“)&& 这个方法,它里面会触发属性读取事件:
/// &summary&
/// 获取属性值
/// &/summary&
/// &typeparam name=”T”&值的类型&/typeparam&
/// &param name=”propertyName”&属性名称&/param&
/// &returns&属性值&/returns&
protected T getProperty&T&(string propertyName)
this.OnPropertyGeting(propertyName);
return CommonUtil.ChangeType&T&(PropertyList(propertyName));
/// &summary&
/// 属性获取事件
/// &/summary&
public event EventHandler&PropertyGettingEventArgs& PropertyG
/// &summary&
/// 获取属性的时候
/// &/summary&
/// &param name=”name”&&/param&
protected virtual void OnPropertyGeting(string name)
if (this.PropertyGetting != null)
this.PropertyGetting(this, new PropertyGettingEventArgs(name));
/// &summary&
/// 属性获取事件
/// &/summary&
public class PropertyGettingEventArgs : EventArgs
private string _
/// &summary&
/// 属性名称
/// &/summary&
public string PropertyName
get { return _ }
set { _name = }
/// &summary&
/// 以属性名称初始化本类
/// &/summary&
/// &param name=”name”&&/param&
public PropertyGettingEventArgs(string name)
this.PropertyName =
而OQL实例对象,正是订阅了EventHandler&PropertyGettingEventArgs& 事件:
public OQL(EntityBase e)
currEntity =
//其它代码略
e.PropertyGetting += new EventHandler&PropertyGettingEventArgs&(e_PropertyGetting);
void e_PropertyGetting(object sender, PropertyGettingEventArgs e)
//其它代码略
所以,在属性获取事件中,我们可以通过PropertyGettingEventArgs.PropertyName 得到实体类属性对应的字段名称,因此,我们就可以方便的做到选取我们本次查询需要的字段:
Users user = new Users();OQL q0 = OQL.From(user)
.Select(user.ID, user.UserName, user.RoleID)
对应的SQL语句:
[UserName],
[RoleID]FROM [LT_Users]
这样,我们无需使用委托,也不需要Lambda表达式,更不需要表达式树,就能够直接获取到要查询的表名称和字段名称,写法比Linq更简洁,当然,代价是要先实例化实体类。
2.1,属性获取事件的变化
在事件方法e_PropertyGetting 中,我们看看PDF.NET Ver 5.0前后的变化:
Ver 4.X 以前:
void e_PropertyGetting(object sender, PropertyGettingEventArgs e)
doPropertyGetting(((EntityBase)sender).TableName, e.PropertyName);
private void doPropertyGetting(string tableName, string propertyName)
if (isJoinOpt)
string propName = “[“ + tableName + “].[“ + propertyName + “]“;
this.currJoinEntity.AddJoinFieldName(propName);
string field = this.joinedString.Length & 0 ? tableName + “].[“ + propertyName : propertyN
if (!hasSelected && !selectedFields.Contains(field))
selectedFields.Add(field);
this.getingTableName = tableN
this.getingPropertyName = propertyN
在doPropertyGetting& 方法中,区分是否有实体类连接查询,来处理不同的表名称和字段名称,这里看到连接查询的时候没有为表加上别名,而是直接使用了&表名称.字段名称&这种表示字段的形式。同时,将当前获取到的表字段名,马上赋值给getingPropertyName 变量。这带来了一个问题,属性字段名称必须马上被使用,否则就会出问题。由于不同的情况使用属性字段的时机不一样,为了处理这些不同的情况加入了各种Case下的处理代码,比如将Select方法要使用的属性字段名称保存到列表& selectedFields 中。这种处理方法无疑大大增加了代码的复杂度。
Ver 5.0 版本的改进
前面说到属性获取到的属性字段名称必须马上被使用,否则就会出问题。如果我们不论何种情况,都将这个属性字段名先保存起来再使用呢?使用队列?链表?堆栈?这些集合都可以,但在编译原理中,对表达式的处理都是使用堆栈来做的,其中必有它的好处,以后会体会到。
下面是属性获取事件代码:
void e_PropertyGetting(object sender, PropertyGettingEventArgs e)
TableNameField tnf = new TableNameField()
Field = e.PropertyName,
Entity = (EntityBase)sender,
Index=this.GetFieldGettingIndex()
fieldStack.Push(tnf);
这里直接将属性字段名存在TablenameField 结构的Field字段中,然后将这个结构压入堆栈对象fieldStack 中,需要的时候在从堆栈中弹出最新的一个&TableNameField 结构。
这样,不论是OQL的Select方法,Where方法还是OrderBy方法,都能够使用统一的堆栈结构来获取方法使用的属性字段了。
2.3,统一属性获取事件
除了OQL本身需要&属性获取事件&,OQL关联的OQLCompare对象,OQLOrder对象,都需要处理属性获取事件,比如之前实例化OQLCompare对象:
/// &summary&
/// 使用一个实体对象初始化本类
/// &/summary&
/// &param name=”e”&&/param&
public OQLCompare(EntityBase e)
this.CurrEntity =
//this.CurrEntity.ToCompareFields =
this.CurrEntity.PropertyGetting += new EventHandler&PropertyGettingEventArgs&(CurrEntity_PropertyGetting);
/// &summary&
/// 使用多个实体类进行连接查询的条件
/// &/summary&
/// &param name=”e”&&/param&
/// &param name=”joinedEntitys”&&/param&
public OQLCompare(EntityBase e, params EntityBase[] joinedEntitys)
this.CurrEntity =
this.CurrEntity.PropertyGetting += new EventHandler&PropertyGettingEventArgs&(CurrEntity_PropertyGetting);
//处理多个实体类
if (joinedEntitys != null && joinedEntitys.Length & 0)
this.joinedEntityList = new List&EntityBase&();
foreach (EntityBase item in joinedEntitys)
this.joinedEntityList.Add(item);
item.PropertyGetting += new EventHandler&PropertyGettingEventArgs&(CurrEntity_PropertyGetting);
属性获取事件处理方法:
void CurrEntity_PropertyGetting(object sender, PropertyGettingEventArgs e)
if (this.joinedEntityList != null)
this.currPropName = “[“ + ((EntityBase)sender).TableName + “].[“ + e.PropertyName + “]“;
this.currPropName = “[“ + e.PropertyName + “]“;
//propertyList.Add(e.PropertyName);
之所以要在OQLCompare等对象中也要处理属性获取事件,是为了OQLCompare能够独立使用,但这带来一些问题:
各地的属性获取事件处理代码类似,代码有冗余;
没有体现出OQL跟OQLCompare 、OQLOrder对象之见的聚合性,呈现出松散的结构,因此可能出现OQLCompare使用的实体类在OQL中没有使用,从而产生错误的查询;
OQLCompare中的的字段名与OQL缺乏相关性,因此只能通过&表名称.字段名称&这种形式来使用属性字段名,无法使用别名。
Ver 5.0的解决办法:
在OQL对象上,定义一些方法供OQL的关联子对象来访问需要的属性字段名信息:
/// &summary&
/// 从堆栈上只取一个字段名
/// &/summary&
/// &returns&&/returns&
protected internal string TakeOneStackFields()
//其它代码略
TableNameField tnf = fieldStack.Pop();
return GetOqlFieldName(tnf);
2.4,SQL的语法结构
SQL是结构化查询语言,它自身也是非常结构化的,每一个查询都有固定的语法结构,以Select为例,它可以有多种形式的写法:
SELECT Field1,Field2…
FROM [Table]
——————
SELECT Field1,Field2…
FROM [Table]
WHERE Condition
——————
SELECT Field1,Field2…
FROM [Table]
WHERE Condition
ORDER BY FieldOrder
——————
SELECT FieldGroup
FROM [Table]
WHERE Condition
GROUP BY FieldGroup
ORDER BY FieldOrder
—————–
SELECT FieldGroup
FROM [Table]
WHERE Condition
GROUP BY FieldGroup
HAVING havingExp
ORDER BY FieldOrder
——————
SELECT Field1,Field2…
FROM [Table1]JOIN [Table2] ON [Table1].PK=[Table2].FK
WHERE Condition
ORDER BY FieldOrder
——————
SELECT Field1,Field2…
FROM [Table1],[Table2]
WHERE [Table1].PK=[Table2].FK And OtherCondition
ORDER BY FieldOrder
——————
仔细观察,万变不离其宗,上面的写法其实都是以下关键字所在层次结构在不同情况下的组合而已,除了Select是必须的:
SELECT FROM
2.5,OQL的层次结构
如果要以面向对象的方式来实现SQL这个关键字层次结构,我们必须将相关的关键字作为方法,定义在合适的对象中,然后靠对象的层次结构,来限定正确的&SQL&结构,为此,我们先重新来定义一下OQL使用的接口IOQL和关联的接口的层次定义:
public interface IOQL
OQL1 Select(params object[] fields);
public interface IOQL1 : IOQL2
//OQL End { }
//OQL3 GroupBy(object field);
//OQL4 Having(object field);
//OQL4 OrderBy(object field);
OQL2 Where(params object[] fields);
public interface IOQL2 : IOQL3
//OQL End { }
OQL3 GroupBy(object field);
//OQL4 Having(object field);
//OQL4 OrderBy(object field);
public interface IOQL3 : IOQL4
OQL4 Having(object field);
//OQL End { }
//OQL4 OrderBy(object field);
public interface IOQL4
OQL END { get; }
OQLOrderType OrderBy(object field);
然后,让OQL实现IOQL,在定义其他OQL子对象来实现其它子接口。由于对象比较多,还是通过一个对象结构图来看更方便:
&图1:OQL接口层次图
&图2:OQL体系结构图
&2.6 OQLCompare–比较对象的组合模式
SQL的查询条件可以很简单,也可以很复杂,比如下面的复合查询条件:
FROM [LT_Users]
INNER JOIN [LT_UserRoles] T0
M.[RoleID] = T0.[ID]
M.[UserName] = @P0 AND
M.[Password] = @P1
T0.[RoleName] = @P2 ) OR
M.[UserName] = @P3 AND
M.[Password] = @P4
M.[LastLoginTime] & @P5 )
这个查询条件分为2组条件,然后第二组查询内部又包含2组查询,从括号层数来说,仅仅有3层,但看起来已经够复杂了。实际项目中,我曾遇到过用5000行业务代码来构造SQL查询条件的情况,不要吃惊,的确是5000行业务代码,当然不是说SQL条件有5000行,当然也可以想象到,最终生成的SQL查询条件的长度不会小于50行。这样复杂的查询条件,如果用拼接SQL字符串的方式来完成,工作量是不可想象的,维护起来也是非常困难。但是,我们可以利用OQL的查询条件对象OQLCompare来完成,因为它实质上是一个组合对象,即N多个OQLCompare组合成一个OQLCompare对象,不过为了实现方便,我们规定每个OQLCompare对象下面存放2个子对象,也就是建立一个二叉树来存储所有的比较对象:
public class OQLCompare{
//其它代码略
protected OQLCompare LeftNode { get; set; }
protected OQLCompare RightNode { get; set; }
protected CompareLogic Logic { get; set; }
protected bool IsLeaf
return object.Equals(LeftNode, null) && object.Equals(RightNode, null);
protected string ComparedFieldN
protected string ComparedParameterN
protected CompareType ComparedT
还是用一张图来看看查询条件的构成比较直观:
&图3:OQLCompare 对象树
该图的内容,说明了构造上面的SQL条件的OQLCompare比较对象的树型结构,我们规定,每个节点下面只有左节点和右节点,左节点优先,左右子节点都可以是空,如果符合该条件,则当前节点为叶子结点,否则为枝节点。
从上图可以很容易发现,其实这就是一个&组合模式&,而组合模式的每个节点都具有相同的行为和特性,所以,我们可以构建非常复杂的组合体系,最终构造超级复杂的查询条件。
2.7,条件表达式的括号问题
&括号是控制表达式计算顺序的重要手段,对于逻辑表达式,使用AND,OR 来连接两个子表达式,如果AND,OR同时出现,则需要用括号来改变表达式元素计算的顺序。C,C++,C# 对表达式都是&左求值计算&的,这是一个很重要的概念,某些程序语言可能是&右求值计算&的。如果表达式中有括号,那么前面的计算将挂起,计算完括号内的结果后,再继续处理表达式的剩余部分。因此,我们可以把括号看作一个&树枝节点&,而括号内最内层的节点,为叶子结点,因此,上面我们构造的OQLCompare对象树,在输出SQL条件字符串的时候,可能是这个样子的:
FROM [LT_Users]
INNER JOIN [LT_UserRoles] T0
M.[RoleID] = T0.[ID]
( M.[UserName] = @P0 AND
M.[Password] = @P1)
T0.[RoleName] = @P2 ) OR
(M.[UserName] = @P3 AND
M.[Password] = @P4)
M.[LastLoginTime] & @P5 ) )
假设条件表达式需要对10个字段的比较内容进行AND 判断,那么将会嵌套10-1=9 层括号。
不要小看这个问题,前面我说到的那个5000行业务代码构建SQL查询条件的事情,就曾经发生过构造了128层括号的事情,最终导致SQLSERVER报错:
查询条件括号嵌套太多,查询分析器无法处理!
那么括号怎么化简呢?
这个得从表达式的逻辑语义上去分析:
(A AND B) AND C
&==& A AND B AND C
(A OR B) OR C
&==& A OR B OR C
(A ND B) AND (C AND D)
&==& A AND B AND C AND D
(A OR B) OR (C OR D)
&==& A OR B OR C OR D
所以,我们可以检查&子树枝节点&的逻辑比较类型,如果它的类型与当前节点的逻辑比较类型相同,那么对子树枝节嗲的处理就不需要使用括号了。
可以通过哦递归过程,处理完所有的子节点的括号问题,从而最终得到我们看起来非常简单的条件表达式。
&(本文篇幅太长,未完待续)
本文链接:,转载请注明。}

我要回帖

更多关于 jdk8 lambda表达式 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信