绝大多数报表项目的数据库中,除了支撑系统运行的业务数据表之外,实际业务还可能产生很多中间表。业务数据表是报表系统的核心和基础,是支持报表系统运行的持久化数据层,例如:销售报表系统中的订单、客户、产品等。而中间表大多则是计算和生成报表的中间计算过程,一般只支持临时性或者局部性的需求,而且名字经常是五花八门,不利管理和维护。
按道理说,在一个“健康”的业务系统中,业务数据表应该占大多数,报表中间表应该只占少数。但实际情况却恰恰相反,在有些运行了较长时间的报表系统中,常常可以看到报表中间表达到几千个或者更多,而业务数据表只有一两百个。
对于关系型数据库来说,其综合成本与数据表的数量、数据量密切相关。数据库中报表中间表过多会导致以下问题:
表越多,数据量越大,数据库的负载压力就越大,这会直接引起性能下降。
数据库压力达到一定程度就必须扩容,项目成本会增加不少。
关系数据库是扁平结构,不能以多级目录的形式来管理数据,因此只适合管理数量较少的表。而中间表命名随意,非常容易形成大量意义混淆的表名,可管理性差。
表多、数据量大,直接造成数据库管理和维护成本的增加。
那么,该如何减少报表开发中的数据库中间表呢?分库,是目前报表项目中解决数据库中间冗余数据常见的做法。但是,不管是将数据库分成多个,抑或是构建专门的数据仓库,本质上还是用传统关系型数据库来承载中间数据,因此,上边所说的各种问题依然存在,得不到本质的解决。
因此,最理想的思路就是,能否将中间数据从数据库中移出来呢?比如小结果集直接计算后返回,大结果集放到硬盘文件上?如果能这样做,好处非常明显:
1、降低了数据库的压力,让报表系统运行更快。
数据库不必因为报表中间表的原因而扩容,可以降低项目成本。
文件可以按照业务种类、模块关系、时间顺序进行多级目录管理,可管理性高。
数据表少了,数据库管理和维护自然变得容易,运维成本明显降低。
但是,中间数据外置的做法对于多数程序员来说,只是个美好的愿望而已。他们不是“不愿”把中间表放到数据库外,而是“不能”放到库外。因为,数据一旦离开了数据库,就失去了“计算能力”。例如:某项目中产生了一个中间表 temp2009sales,存放 2009 年客户 - 订单数据,并已按照客户分组。如果数据在数据库中,对这个表按照客户排序很容易实现,但是导出到文本文件后就没办法排序了,因为文件本身没有计算能力。
所以,中间数据外置的关键就在于能否实现基于文件的数据计算能力,特别是通用的计算能力。如果采用润乾集算器,其集算引擎可以使文件拥有计算能力,从而实现复杂计算与报表展现的彻底分离,完美实现程序员“把报表中间数据从数据库中移出来”的愿望。下图显示了引入润乾报表和集算引擎前后的报表系统结构对比:
由于润乾报表能够很好地支持异构数据源,上面提到的中间数据文件 temp2009sales 虽然已经放到了库外,但是依然可以和数据库中其他的业务表进行关联计算,用于最终生成报表,例如下图这张“某公司客户累计销售额与去年全年销售额对比报表”:
这张报表中,客户、订单数、销售额都是直接从数据库的当年数据计算得到的,即 2010 年 1 月 -10 月的数据;2009 年全年的订单数、销售额则是从文件系统中的 temp2009sales.b 文件中读取;而“销售额 / 去年销售额”则是利用今年和去年的数据计算得到的。
下面我们就重点看一下这个报表的开发过程。(报表上部的查询按钮是集算报表提供的“参数模板”功能,具体做法参见教程,这里不再赘述。)
1、数据文件准备:
提前用集算器从数据库中读取 2009 年的销售数据,计算好之后,以集算器的二进制编码方式导出到文件 temp2009sales.b 中。中间数据制作好后,数据库中 2009 年的数据就可以移除了,释放宝贵的数据库空间。
2、编写集算器脚本 salesProportion.dfx:
其中脚本中用到参数:argyear(要查询的年份),argmonth(要查询的月份)。
A | |
---|---|
1 | =connect(“db”) |
2 | =A1.query(“select client,count(orderid) c,sum(amount) s from sales where year(orderdate)=? and month(orderdate)<=? group by client”,argyear,argmonth) |
3 | =file(“D:/files/salesproportion /temp”+string(argyear-1)+“sales.b”).import@b() |
4 | =A3.align(A2:CLIENT,CLIENT) |
5 | =A2.new(CLIENT,C:COUNT,S:TOTAl,A4(#).C:lastCOUNT,A4(#).S:lastTOTAL,S/A4(#).S:PROPORTION) |
6 | result A5 |
代码说明:
A1:连接预先配置好的数据源 demo。
A2:从数据库中计算取出要查询的年份订单数、销售额。
A3:从前一年的数据文件中取出数据。
A4:将 A3 中的数据按照 A2 中的 CLIENT 字段对齐,A2 中有但 A3 中没有的则补空行。
A5:利用 A2 来生成新的序表。其中增加了 A4 的对应行数据,比如 A4(#).C:lastCOUNT 就是 A4 的对应行中取出 C 字段,其中 #是 A2 的当前行号。
A6:关闭数据库连接。
A7:向报表返回结果集。
3、在润乾报表中定义报表参数(argyear、argmonth)和计算数据集:
上图中,参数名是指 dfx 定义的参数名称,参数值是指报表提交给集算引擎的值。配置好后,就会将报表的两个参数的值传递给集算器的同名参数。
4、设计报表,如下图:
输入参数计算后,即可得到前面希望的报表了。
更多复杂计算相关问题请查看:复杂计算相关问题分类导航