该帖已经被评为精华帖
|
|
---|---|
作者 | 正文 |
发表时间:2006-08-12
这里的{mean1}, {mean2}可以是任意sql表达式?从java bean来?
可是要知道,mean1很可能是这样的复杂表达式: avg(t1.value-isnull(t2.value,t3.value)) 这里面,value是列名字,t1, t2, t3是后面跟着的某些表的别名。难道这些东西也会在java bean里面?java bean里面难道不是纯的数据,而是sql表达式(甚至还包括只在局部上下文才有意义的表别名)? |
|
返回顶楼 | |
发表时间:2006-08-12
1、
一条SQL,为什么还要考虑他是不是能够重用? 2、 SQL语句肯定会与具体的字段相关,但是可以是标准的SQL,怎么就会“往往就绑定数据库”。这是本来就是写SQL的时候需要注意避免的。 3、 复杂SQL本来就是难以避免的事情,一般情况下,实现复杂的业务需求,写出复杂的业务需求,的确困难。但是要以组合子的思维模式去干,往往更复杂。 |
|
返回顶楼 | |
发表时间:2006-08-13
sql很可能不是一条阿。若干条长得很相似的sql,只有一点不同(比如,top n不同,order by不同,where clause不同)之类,还是很有可能的。当然可以不考虑重用,就忍受点代码重复就是。我只是对代码重复深恶痛绝,无论是sql,脚本,还是xml配置。
标准sql自然好,不过往往现实不是那么理想的阿。比如sql的coalesce函数,isnull()函数,用的很多把?left join, right join是标准,但是我记得oracle的老版也不支持的。top n常用吧?也不是标准。 比如某个字段名叫numeric,它可能是某个dbms的关键字,你不需要用"[]"括起来?对了"[]"是标准sql的quote,还是mssql自己的? 组合子的思维,并不是强迫从用户的角度从基本的组合子通过几十层嵌套组合复杂sql,我的想法是,可以配合一个parser,基本的骨架仍然通过写sql,但是把它parse成组合子。 比如: Relation base = RelationParser.parse("select a,b from table1 t1 inner join table2 t2 on t1.id1 = t2.id2");; Relation sql1 = base.top(10);; Relation sql2 = base.where("[date]<$dateparam");.bindParam("dateparam", dateobj);; Relation sql3 = sql2.orderby("[date]");; ... sql1, sql2, sql3代表三个相似但是不同的sql。 这样也不复杂啊。然后也可以得到代码重用的好处。 |
|
返回顶楼 | |
发表时间:2006-08-13
ajoo 写道 sql很可能不是一条阿。若干条长得很相似的sql,只有一点不同(比如,top n不同,order by不同,where clause不同)之类,还是很有可能的。当然可以不考虑重用,就忍受点代码重复就是。我只是对代码重复深恶痛绝,无论是sql,脚本,还是xml配置。
标准sql自然好,不过往往现实不是那么理想的阿。比如sql的coalesce函数,isnull()函数,用的很多把?left join, right join是标准,但是我记得oracle的老版也不支持的。top n常用吧?也不是标准。 比如某个字段名叫numeric,它可能是某个dbms的关键字,你不需要用"[]"括起来?对了"[]"是标准sql的quote,还是mssql自己的? 组合子的思维,并不是强迫从用户的角度从基本的组合子通过几十层嵌套组合复杂sql,我的想法是,可以配合一个parser,基本的骨架仍然通过写sql,但是把它parse成组合子。 比如: Relation base = RelationParser.parse("select a,b from table1 t1 inner join table2 t2 on t1.id1 = t2.id2");; Relation sql1 = base.top(10);; Relation sql2 = base.where("[date]<$dateparam");.bindParam("dateparam", dateobj);; Relation sql3 = sql2.orderby("[date]");; ... sql1, sql2, sql3代表三个相似但是不同的sql。 这样也不复杂啊。然后也可以得到代码重用的好处。 good. template 的方式一样,也需要parse。只是通用template的语法很简单,不需要识别sql的很多元素。 select // BEGIN DYNAMIC: top top {n} // END DYNAMIC: top a,b from table1 t1 inner join table2 t2 on t1.id1 = t2.id2 // BEGIN DYNAMIC: date where date > {date} // END DYNAMIC: date -------------------- Object model = baseModel(); Object topModel = makeTopModel(model, 10); Object whereModel = makeWhereModel("date", date); |
|
返回顶楼 | |
发表时间:2006-08-13
如果说仅仅是技术试验,那就没所谓了。
如果是真实项目,利用TDD这种实现确实是复杂了。无论如何比起容易理解的XQL来说,这种组合的方式,都会带来,理解上的问题。 其实即使是需要动态的得到XQL,其变化也是有限。比如:针对不同用户显示不同授权查询结果的需求,就仅仅可能需要变化Where子句的某个within。 具体问题,具体分析,每个可以"组合"的地方,我们有需求,需要它变化么?或者说,有Test存在么?如果不能说出测试,那么这种设计就是画蛇添足。 对于技术的热爱,往往会使我们选择复杂的设计,从这点来说,TDD无疑是很好的清醒剂。 |
|
返回顶楼 | |
发表时间:2006-08-13
buaawhl 写道 ajoo 写道 sql很可能不是一条阿。若干条长得很相似的sql,只有一点不同(比如,top n不同,order by不同,where clause不同)之类,还是很有可能的。当然可以不考虑重用,就忍受点代码重复就是。我只是对代码重复深恶痛绝,无论是sql,脚本,还是xml配置。
标准sql自然好,不过往往现实不是那么理想的阿。比如sql的coalesce函数,isnull()函数,用的很多把?left join, right join是标准,但是我记得oracle的老版也不支持的。top n常用吧?也不是标准。 比如某个字段名叫numeric,它可能是某个dbms的关键字,你不需要用"[]"括起来?对了"[]"是标准sql的quote,还是mssql自己的? 组合子的思维,并不是强迫从用户的角度从基本的组合子通过几十层嵌套组合复杂sql,我的想法是,可以配合一个parser,基本的骨架仍然通过写sql,但是把它parse成组合子。 比如: Relation base = RelationParser.parse("select a,b from table1 t1 inner join table2 t2 on t1.id1 = t2.id2");; Relation sql1 = base.top(10);; Relation sql2 = base.where("[date]<$dateparam");.bindParam("dateparam", dateobj);; Relation sql3 = sql2.orderby("[date]");; ... sql1, sql2, sql3代表三个相似但是不同的sql。 这样也不复杂啊。然后也可以得到代码重用的好处。 good. template 的方式一样,也需要parse。只是通用template的语法很简单,不需要识别sql的很多元素。 select // BEGIN DYNAMIC: top top {n} // END DYNAMIC: top a,b from table1 t1 inner join table2 t2 on t1.id1 = t2.id2 // BEGIN DYNAMIC: date where date > {date} // END DYNAMIC: date -------------------- Object model = baseModel(); Object topModel = makeTopModel(model, 10); Object whereModel = makeWhereModel("date", date); 我现在就是这么做的。只不过是用jaskell的string interpolation功能。这个方法挺方便的。不过它在重用sql上还是不够舒服。 比如你举这个例子,基本上就是说,需要统一估计所有可能的query逻辑变化。 1。可能需要不同的top n,所以放一个top {n}在那里。(问题,如果我的某个query不要top n怎么办?把n设置成-1?) 2。可能需要对date做filter,所以方一个dynamic sector在那里。 3。等等,某些其他场合可能还要对company_id做filter,再方一个dynamic section吧。 4。不好,另外一些场合,我不需要top n,而是需要group by,怎么办? query的变化理论上是无穷无尽的,如果你每发现一个可能变化的地方,都要跑到那个query里面加一段东西,且不说做不做得到(实际上我相信你无法应对所有变化的),即使做到了,这个query也会变成超级复杂的怪物。它的存在只是为了应对理论上可能出现的最复杂情况,代价是牺牲了更频繁出现的简单情况。 比如我90%的情况就是一个最简单的query,没有top,没有where,没有orderby,没有group by,此时却仍然不得不面对这个超级灵活无所不能的template。 这是运用复杂情况来涵盖简单情况,就是采用了OO的"归纳"方法论的产物——把所有的需求罗列出来,然后一一解决。而co的精神则是组合简单来应对复杂,用简单无比的组合子演绎复杂情况。 后者的好处是,简单情况的东西不会受复杂情况影响。我上面那个base query,就是一段最简单的query,后面不管有多少复杂的变体,这个base始终保持稳定,简单,不受影响。 说了一大堆。“简单”,“复杂”的,我估计唯一作用就是把人绕晕了。 或者这么说吧。我上面的base, sql1, sql2, sql3都是有用的。base, sql1和sql2并不是仅仅为了生成sql3的中间步骤。我的真实逻辑很可能是这样的: Relation someLogic();{ if(...);{ return sql1; } else if(...);{ return sql2; } else { return sql3; } } 这里,尽管sql1, sql2, sql3实际上有很多重复代码,但是使用组合子逻辑,代码重复被完全避免了。 采用template,怎么避免代码重复呢? |
|
返回顶楼 | |
发表时间:2006-08-13
是不是可以从domain object的角度
考虑限制组合的范围在如下: 1. where 2. order by 另外还有和domain object无关的 3. 分页 4. top n 这样不考虑select和from,当然也不考虑join了。 这样问题简单了。对于同一domain object其where和order的组合是简单的。 DDD一书中的and,or和not操作是容易支持的。 那么对于那些确实复杂的sql查询怎么办?简单。单独写一个,我的观点是太复杂的东东复用的可能性也不大,日后出现类似的东东可以考虑用OO的继承的方式搞定,CO和OO本来就不是相互排斥的。 不过不考虑join明显是个问题,我估计应用系统中:普通的sql,join的sql以及更复杂的sql语句的比例是4:4:2。只是如果应用specification的话,join处理实在麻烦。 |
|
返回顶楼 | |
发表时间:2006-08-13
ajoo 写道 我现在就是这么做的。只不过是用jaskell的string interpolation功能。这个方法挺方便的。不过它在重用sql上还是不够舒服。 比如你举这个例子,基本上就是说,需要统一估计所有可能的query逻辑变化。 1。可能需要不同的top n,所以放一个top {n}在那里。(问题,如果我的某个query不要top n怎么办?把n设置成-1?) 2。可能需要对date做filter,所以方一个dynamic sector在那里。 3。等等,某些其他场合可能还要对company_id做filter,再方一个dynamic section吧。 4。不好,另外一些场合,我不需要top n,而是需要group by,怎么办? query的变化理论上是无穷无尽的,如果你每发现一个可能变化的地方,都要跑到那个query里面加一段东西,且不说做不做得到(实际上我相信你无法应对所有变化的),即使做到了,这个query也会变成超级复杂的怪物。它的存在只是为了应对理论上可能出现的最复杂情况,代价是牺牲了更频繁出现的简单情况。 比如我90%的情况就是一个最简单的query,没有top,没有where,没有orderby,没有group by,此时却仍然不得不面对这个超级灵活无所不能的template。 这是运用复杂情况来涵盖简单情况,就是采用了OO的"归纳"方法论的产物——把所有的需求罗列出来,然后一一解决。而co的精神则是组合简单来应对复杂,用简单无比的组合子演绎复杂情况。 后者的好处是,简单情况的东西不会受复杂情况影响。我上面那个base query,就是一段最简单的query,后面不管有多少复杂的变体,这个base始终保持稳定,简单,不受影响。 说了一大堆。“简单”,“复杂”的,我估计唯一作用就是把人绕晕了。 或者这么说吧。我上面的base, sql1, sql2, sql3都是有用的。base, sql1和sql2并不是仅仅为了生成sql3的中间步骤。我的真实逻辑很可能是这样的: Relation someLogic();{ if(...);{ return sql1; } else if(...);{ return sql2; } else { return sql3; } } 这里,尽管sql1, sql2, sql3实际上有很多重复代码,但是使用组合子逻辑,代 码重复被完全避免了。 采用template,怎么避免代码重复呢? 代码重复容易避免。 Object someLogic();{ if(...);{ return model1; } else if(...);{ return model2; } else { return model3; } } Query template的复杂度无法避免。 ajoo的SQL parser是专门解决SQL,对这个问题处理最好。如果真遇到这种情况,打算引入这个SQL parser。ajoo的代码是我长期学习的资料。 可能不好的地方:Parser的语法定义比较麻烦,需要覆盖SQL的语法,SQL语法变化,Parser也需要变化。等。 jfun\parsec\mssql下面有几十个AST文件。 |
|
返回顶楼 | |
发表时间:2006-08-14
不会那么麻烦的。这个sql不需要支持所有的sql语法。也不需要针对某个dbms的变化而变化。只要针对ansi sql的标准就好。
比如select * from a, b就可以不支持。反正没有兼容问题。 parser只负责生成语法树。然后分析语法树。创建具体query的那个类(一般会是一个AstVisitor之类的),需要针对不同数据库写不同的实现,甚至同一个数据库不同版本之间也要有所变化。 但是我们至少可以保证sql的稳定性。 我那个parser不能直接用的。它只是一个demo,是直接针对mssql写的,首先并不全,其次有些挺费劲的语法变体这个sql组合子(我叫它jrc——java relational combinator)没有必要支持。 这回再写parser,我会用jparsec的generic版本,应该代码好看些。 |
|
返回顶楼 | |
发表时间:2006-08-14
引用 Object someLogic(){
if(...){ return model1; } else if(...){ return model2; } else { return model3; } } 还是那个问题。莫非这些model对象里面包含sql语法?model1包含“我不要top”,model2包含“需要top,需要where”,model3包括“需要top,需要where,需要order by”,还也许有model4包括“我只要col1, col2, diff=col1-col2”这种逻辑信息? 还不是很明白代码重复如何避免的。 |
|
返回顶楼 | |