论坛首页 Java企业应用论坛

面向组合子编程实验-SQL组合查询条件的简单实现

浏览 37521 次
该帖已经被评为精华帖
作者 正文
   发表时间: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表达式(甚至还包括只在局部上下文才有意义的表别名)?
0 请登录后投票
   发表时间:2006-08-12  
1、
一条SQL,为什么还要考虑他是不是能够重用?
2、
SQL语句肯定会与具体的字段相关,但是可以是标准的SQL,怎么就会“往往就绑定数据库”。这是本来就是写SQL的时候需要注意避免的。
3、
复杂SQL本来就是难以避免的事情,一般情况下,实现复杂的业务需求,写出复杂的业务需求,的确困难。但是要以组合子的思维模式去干,往往更复杂。
0 请登录后投票
   发表时间: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。
这样也不复杂啊。然后也可以得到代码重用的好处。
0 请登录后投票
   发表时间: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);
0 请登录后投票
   发表时间:2006-08-13  
如果说仅仅是技术试验,那就没所谓了。
如果是真实项目,利用TDD这种实现确实是复杂了。无论如何比起容易理解的XQL来说,这种组合的方式,都会带来,理解上的问题。
其实即使是需要动态的得到XQL,其变化也是有限。比如:针对不同用户显示不同授权查询结果的需求,就仅仅可能需要变化Where子句的某个within。
具体问题,具体分析,每个可以"组合"的地方,我们有需求,需要它变化么?或者说,有Test存在么?如果不能说出测试,那么这种设计就是画蛇添足。
对于技术的热爱,往往会使我们选择复杂的设计,从这点来说,TDD无疑是很好的清醒剂。
0 请登录后投票
   发表时间: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,怎么避免代码重复呢?
0 请登录后投票
   发表时间: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处理实在麻烦。
0 请登录后投票
   发表时间: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文件。
0 请登录后投票
   发表时间: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版本,应该代码好看些。
0 请登录后投票
   发表时间: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”这种逻辑信息?

还不是很明白代码重复如何避免的。
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics