用面向对象的思想简化MIS系统中的查询代码(一)——问题

      MIS系统中的数据查询时必不可少的。而且大部分都是多表查询,这时候SQL语句中的Join就显得尤为重要。再加上现在的O-R mapping,更是给查询带来了一个不大不小的麻烦(我不是说O-R mapping的坏话)。以下的部分将用一个本人认为很典型的例子说明一下。

      数据库结构如下:


      用户界面如下:

      由数据库结构和界面的grid可以很容易想到,直接写上

select FACILITY.*, PROJECT.CODE as PROJECT_CODE, PROJECT.CN_NAME as PROJECT_CN_NAME, SITE.NAME as SITE_NAME, SITE.CID as SITE_CID,
FACILITY_CATALOG.CN_NAME as FACILITY_CATALOG_NAME 
from FACILITY left outer join PROJECT on FACILITY.MASTER_ID = PROJECT.ID left outer join SITE on PROJECT.SITE_GUID = SITE.ID left outer FACILITY_CATALOG on FACILITY.CATALOG_GUID = FACILITY_CATALOG.ID

。然后在后头写上where搜索里选择的条件。这似乎没什么难度吧。

      这个项目典型的地方是使用了一个可爱的框架,绑定在grid上的不是DataTable而是一个EntityCollection,而且这里还用了延迟加载,即使在SQL语句中写上了PROJECT.CODE, PROJECT.CN_NAME,由于延迟加载的关系都被忽略了(Facility这个实体对象里包含MASTER_ID,利用延迟加载来获取实体对象Project)。所以SQL语句就得写成了

select FACILITY.* from FACILITY left outer join PROJECT on FACILITY.MASTER_ID = PROJECT.ID left outer join SITE on PROJECT.SITE_GUID = SITE.ID left outer FACILITY_CATALOG on FACILITY.CATALOG_GUID = FACILITY_CATALOG.ID

至于后面的条件还是和前面一样加。我们可以想象一下这么做的问题:1、虽然用户只选择了站点这个条件,但是FACILITY和FACILITY_CATALOG进行了Join。2、如果用户只选择了设备编目,SQL语句就多了FACILITY和PROJECT、SITE的Join。当然啦,看到这里有人可能会说,我们用的框架没有这个问题或者你不会自己用if做判断啊。换框架没有必要,这里只是拿出一个典型的例子,相信就算换了框架,在开发过程中还是会遇到类似问题的。至于用if进行判断,同样也有麻烦,这个例子里的表比较少,写起判断没什么太大问题。关键是用户选择工程名称时你得加入FACILITY和PROJECT的Join,用户选择站点名称时你除了加入FACILITY和SITE的Join外,还必须先添加FACILITY和PROJECT的Join,否则就出错没商量。

      不知大家看到这个例子的特殊性没?本人见过一个项目组用这个框架的时候,一个SQL语句里一次性写了28个Join(自己汗一下先,怎么设计数据库的,虽说是该有不该有的都写了,也没有这么多吧)。当然这不排除数据库设计和框架的问题,其中还有一部分原因就是懒人多,表多条件多的时候谁都不喜欢一个if一个if拼SQL语句。说了这么多,总结起来就是“按需Join的问题”。如果能有一个工具能实现,一次性添加所需的所有东西,然后自动根据需要生成SQL语句,该多好(或者说方便多了,至少本人是这么认为的)?

      如果SQL语句进行分解。可以划分为显示的字段定义、表关联定义、条件定义、排序、分组等几个部分。因此,本人对SQL语句的各部分进行如下划分,并以以下术语命名:
      1、 Fields:输出字段的定义。
      2、 Joins:表关联定义。
      3、 Filters:查询条件定义。
      4、 GroupBys:分组定义。
      5、 Having:Having子句
      6、 Existis:Exists子句。
      7、 OrderBys:排序定义。
      8、 SubQuerys:子查询定义
      9、 UsedTables:查询中所使用的表。
      10、UsedJoins:查询中所使用的联结
      11、 QueryInfo:一条完整的SQL查询语句。
      12、 QueryBuilder:查询生成器。

因此SQL语句可以用如下形式表示:
select (Fileds) from (UsedTables | SubQuerys| UsedJoins] [where [Exists] Filters] [group by GroupBys] [having Having] [order by OrderBys]
注:黑体字表示SQL保留字。“()”表示必填项。“|”表示可在其中选择一项。“[]”表示可选项。

      经过分解后可以得出这样的结论,UsedTables中的表由所有的Fields、Exists、Filters、GroupBys、Having、OrderBys中所使用的表组成,SubQuerys也可以根据Alias从UsedTables判断是否使用。UsedJoins也可以用同样的方法来实现按需选取,复杂的地方是一个Join可能依赖另一个Join,就像例子里的FACILITY和SITE的那样。另外,Fields为空的时候必须转换为“*”,UsedJoins不为空时From部分用Join,UsedJoins为空时from部分则必须用UsedTables或SubQuerys来代替。

      暂时先到这,眼花脖子酸,下回给出对象图和实现思路。

posted @ 2008-04-23 09:39 陈鹏(偶是坏人) 阅读(1850) 评论(6)  编辑 收藏 网摘 所属分类: 我的道路

  回复  引用  查看    
#1楼2008-04-23 10:47 | SZW      
以前用三层做这些东西的时候和楼主有很多同感,特别是多表查询的时候。楼主给的例子还算比较简单,有的表四五十个字段,条件一多真的要眼花了。
说到OR/M,我想到了如果用了Linq to SQL之后,对于left join之类的问题在开发层面上应该是简化的,但是在效率上仍然得不到保证——MIS里面28个Join的情况在Linq to SQL自动生成SQL语句的情况下简直是不稀奇的……

  回复  引用  查看    
#2楼[楼主]2008-04-23 11:04 | 陈鹏      
28个查询的结果是1000条记录的表,查询要一分钟以上,这个就比较稀奇了。
暂时我还没有怎么关注Linq。
不过我看了一下,似乎Linq的查询也只是做到了简化,但是好像没法智能地选择需要的Join的地步,如果添加了无用的Join,Linq会自动过滤吗?
我的例子不是为了说明复杂度,而是为了说明可以使用一定的手段优化多表查询的性能。

  回复  引用  查看    
#3楼2008-04-23 11:42 | SZW      
@陈鹏
只要你在后面加.where(),.join()等等,linq to sql不管三七二十一都会用join(默认情况下),比如你写(from c in ctx.Table where c.Id>10 select c).Where(c=>c.Id<100),就会发生join(当然这里的join是最简单的情况了,为了方便说明没有连表),而真正合理的情况,生成的SQL语句应该是和(from c in ctx.Table where c.Id>10 && c.Id<100 select c)相等的,可惜事实并非这样。而前面的(from c in ctx.Table where c.Id>10 select c)自动生成的一些规则,和楼主说的“表示形式”还是有点类似的。这个“智能”环节上有待发展的地方确实还很多。

  回复  引用  查看    
#4楼2008-04-23 14:26 | 镜涛      
Linq确实让数据库的操作变得简单了,不过他生成的query确实过于麻烦,无关条件用的太多!
  回复  引用  查看    
#5楼2008-04-23 15:43 | 金色海洋(jyk)      
有没有试过使用视图呢?
  回复  引用  查看    
#6楼2008-04-24 13:45 | 队长      
Dev控件,PowerDesigner类图,和我们的开发很像啊
博主提出的问题在MIS系统中很普遍
期待ing




发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

[使用Ctrl+Enter键快速提交评论]

0 1166801




相关文章:

相关链接: