报表应用中,如果数据量较大或计算过程较复杂,往往会导致报表数据源准备过慢,从而影响报表性能。这种情况下可以预先计算报表需要的数据,在呈现时直接引用,使得用户在访问报表时可以迅速地获得响应。
一、当前的手段及弊端
由于报表在访问时常常需要参数,因此显然不可能把所有参数组合对应的报表数据源都准备好,所以预先计算通常只生成中间数据,在呈现时仍然要再进行一些后续的简单计算(如过滤、分组汇总、排序等)。不过即便如此,也不太可能完全由报表工具在中间数据基础上完成所有后续运算,因此报表工具通常只能完成一些小数据量的运算。也就是说,存储的中间数据还需要有有再次计算的能力,所以一般情况下会将中间数据以中间表的形式存储在数据库中,以便在呈现时再借用数据库的计算能力。
采用中间表进行预先计算会存在以下弊端:首先,将过多的计算工作交给数据库做,无疑会加大数据库的压力,甚至反而造成性能不升反降;其次,中间表太多容易导致管理混乱,由于数据库采用非层次结构(与文件系统的树形结构不同),因此大量存在的中间表往往会增加数据库管理难度。此外,从数据库中读取较大的中间表还会出现 I/O 瓶颈,同样会导致报表性能不佳。
二、润乾报表的解决方案
润乾报表的预先计算方案(结合集算器实现)不需要使用数据库中间表,从而可以避免上述弊端。润乾报表内置的运算引擎拥有完整的计算能力,一方面可以将中间数据存放在文件中,另一方面还可以对文件进行再计算后作为报表数据源,缩短报表计算时间,提升报表性能。
这种方式看上去和使用数据库中间表类似,都需要事先计算准备数据,但也有很大的不同:第一,不会占用昂贵的数据库空间,不会增加数据库负担;第二,中间数据的组织管理基于文件系统,清晰明了;第三,数据量大时不会出现 IO 瓶颈。
润乾报表能够做到这些,得益于内置了专门用于数据计算的集算引擎。集算引擎与文件系统可实现无缝交互(读入和输出),可以读取多种文件格式,如常见的文本、Excel 等,也包括效率更高的二进制文件,从而使文件具备再计算的能力,轻松实现报表预先计算。
下面用一个例子说明润乾报表进行预先计算的步骤:
1、将需要的中间结果保存成文件
润乾报表支持常见的文本文件格式,例如可以将订单明细分组汇总后的数据直接存放在文本(orderDetail.txt)中。如果希望提高性能,润乾报表还支持更高效的二进制文件格式,比文本能快出 2-5 倍,在集算器中执行如下代码就能将文本文件转换成二进制格式文件。
file(“E:/ 订单明细.b”).export@b(file(“E:/ 订单明细.txt”.cursor())
当然,生成中间数据的过程本身也可以采用集算器实现,但非本文关注点,此处不再详述。
2、 基于中间数据文件生成报表源
润乾报表可以直接基于文件进行再次计算,从而获得报表数据源,比如下面的过滤算法。
脚本中使用的参数及其含义如下:
其中 cols 为选出列名,where 为过滤条件(参数传递时拼接成上述格式),num 为取出的记录数。
脚本内容:
上述脚本基于文件进行过滤和分组汇总操作,其中:
A1:通过文件游标(流式处理)读入大文本数据,此处支持选择列,用户可以根据参数控制选出的数据列。
A2:按照参数进行条件过滤,结果仍然是游标。
A3:按照参数限制记录数,取出游标中的记录。
A4:为报表返回结果集。
上述脚本只处理了一个中间文件,如果需要从多个文件同时查询数据,脚本可以这样编写(以 2 个为例):
其中中间数据文件按年份存储,每 5 年一个。如果查询 1996 到 2005 年间的数据,就需要读取 2 个文件。脚本中 A3 对两个文件的游标进行了纵向拼接,合并成一个游标,然后采用与第一个脚本相同的方式进行处理。当查询的数据范围继续扩大,需要多个文件的时候,可以通过循环的方式将多个文件游标进行纵向拼接合并。
3、设计报表
这一步包括在润乾报表中调用集算脚本,编辑报表表达式完成报表制作等等,这些是报表制作的常规动作了,不再赘述。
与一般优化方式类似,使用预先计算来提高报表性能要充分考虑使用场景。对某些计算过程容易拆分的场景特别适合使用预先计算,如大表汇总数据与其他表做连接,这时可以先将大表汇总数据预先存成文件,再与其他表做连接计算。此外,数据的实时性要求也需要充分考虑,比如在历史查询类的报表中就比较适合使用预先计算,当然润乾报表还提供了其他手段来保证数据的实时性要求。
总结一下,由于一般报表工具不具备文件计算能力,因此要实现预先计算往往要借助数据库进行;而润乾报表拥有完整的文件计算能力,可以避免数据库中间表带来的各种弊端,这一点对用户极具实用价值。
更多性能优化相关问题请查看:性能优化相关问题分类导航