什么是报表的数据库内中间冗余数据?
绝大多数软件项目的数据库中,除了支撑系统运行的业务数据表(business table)之外,还有很多中间表(temporary table)。
业务数据表是软件系统必须的基础数据表,是支持软件系统运行的持久化数据层,如销售管理系统中的部门、角色、权限、订单、客户、产品等等。报表中间表则是计算和生成报表的中间计算过程,中间表的名字经常是五花八门。
按道理说,业务数据表应该是大部分,报表中间表只是小部分。但是,实际情况却恰恰相反。有些运行了较长时间的软件系统中,报表中间表达到几千个或者更多,而业务数据表只有一两百张。
为什么数据库中会出现中间冗余数据?
1、业务数据表结构的设计和报表要展现的数据之间有较大的差异,需要经过多步骤计算才能形成报表数据,每一步计算要把中间结果放在中间表中供下一步计算使用。
2、业务数据表数据量较大,数据库计算报表的时间较长,只好先计算好报表数据,放到中间表中。
3、计算报表的时候需要占用CPU、内存、硬盘IO等资源,会影响业务系统的正常运行,所以要夜间计算好报表数据,放到中间表中。
为什么数据库中的中间冗余数据越来越多?删掉行不行?
在项目建设阶段,需求边界的确定决定了系统功能,也就基本确定了业务数据表的数量,除非系统有结构性的修改,否则业务数据表的数量不会有特别大的变化。而在系统运行过程中,报表的个数随着时间而增加、报表需求也在不断变化,由此导致数据库中的报表中间表越来越多。
甚至有些已经不再使用的报表删掉了,但是中间表却没有删掉。这不仅仅是因为忘记删掉,也可能是因为中间表互相依赖,形成了网状结构,已经没有人能够轻易地删掉了。老的不能删,新的不断建,报表中间表就越来越多,数据量越来越大。
中间冗余数据有什么问题?
关系型数据库的综合成本,是和数据表的数量、数据量相关的。数据库中充斥着各种报表中间表会存在下面的问题:
1、数据表越多,数据量越大,数据库的负载压力越大,会造成性能的下降。
2、数据库压力达到一定程度,数据库就必须扩容,项目成本会增加不少。
3、数据库是扁平结构,不能以多级目录的形式来管理数据,只适合管理数量较少的表。中间表命名随意,形成大量意义混淆的表名,可管理性变差。
4、表多、数据量大,会直接造成数据库管理和维护成本的增加。
如何减少报表开发中的数据库内中间冗余数据?
分库是解决数据库内中间冗余数据的办法之一,也是目前软件项目中常见的做法。但是,不管是将业务库分成多个,还是构建专门的数据仓库,本质上还是用传统关系型数据库来承载中间冗余数据,因此,上边所说的这些问题依然存在。
那么,是否可以将中间冗余数据从数据库中移出来呢?小结果集直接计算后返回,大结果集放到硬盘文件上?这样做的好处非常明显:
1、降低了数据库的压力,让业务系统运行更快。
2、数据库不必因为报表中间表的原因扩容,降低了项目成本。
3、文件可以按照业务种类、模块关系、时间顺序进行多级目录管理,可管理性较高。
4、数据表少了,数据库管理和维护自然变得容易,降低了运维成本。
但是,对于多数程序员来说,他们不是不愿意把中间表放到数据库外,而是不能放到库外,因为数据一旦离开了数据库,就失去了计算能力。例如:某项目中有个中间表temp2013kh-dingdan,是按照客户分组的2013年客户-订单数据。在数据库中,对这个表可以按照客户排序。但是一旦导出到文本文件中,文件本身是没有办法替程序员做排序的。
用集算器esProc可以直接实现复杂计算并向报表工具返回结果集,同时还可以让文件了具备计算能力,实现程序员把报表中间数据从数据库中移出来的愿望。引入集算器之后的报表系统结构如下图:
集算器封装了大量的结构和半结构化的数据计算类库,可以放在数据库和报表工具之间,形成报表计算层。
对于计算非常复杂,需要分步骤计算的过程中产生的数据,如果数据量不大,内存能装下的话,集算器可以直接计算完返回给报表应用,不用制造中间表或者数据文件。
对于必须保存的中间表,例如上边说的temp2013kh-dingdan,可以导出到文本文件(或者集算器的二进制文件)中,利用集算器的file和import函数导入进来,再利用集算器的sort函数完成排序。集算器提供了完备的类库,包括:分组、循环、排序、过滤、集合运算、有序计算等等。
由于集算器支持异构的数据源,中间表temp2013kh-dingdan也可以和数据库中其他的业务表进行关联计算。例如,temp2013kh-dingdan中存的是客户编号,如果报表中需要展现的是客户名称的话,可以在集算器中对数据库中的客户表与文件中中间表进行关联计算。这就实现了移出数据库的中间表与没有移出数据库的统一计算。
集算器部署非常简单,只需要引入一个jar包即可。同时,集算器为报表工具提供了友好的接口,报表可以通过jdbc来调用集算器的脚本,java代码示例如下:
//建立连接
Class.forName(“com.esproc.jdbc.InternalDriver”);
con= DriverManager.getConnection(“jdbc:esproc:local://”);
//调用存储过程,其中dfxName是dfx的文件名
st =( com. esproc.jdbc.InternalCStatement)con.prepareCall(“calldfxName(?)“);
//设置参数
st.setObject(1,”3″);
//执行存储过程
st.execute();
//获取结果集
ResultSet set = st.getResultSet();
值得一提的是,集算器提供功能强大的免费版本,一般的报表应用都可以很好的支持。程序员可以象引入其他Framework一样,逐步引入集算器,达到减少报表在数据库中的中间冗余数据的目的。