你的报表工具会做数据准备吗—报表开发中的深层次问题

前言

现在企业的报表开发大部分都使用报表工具完成,成熟的报表工具提供了丰富的显示设置、图表类型、导出打印等功能可以简化报表开发,非常方便。但在实际报表开发中还是经常碰到一些非常棘手的深层次问题,即使是已经熟练使用报表工具的开发老手也会很挠头。

为什么有了报表工具还会出现这些问题呢?

报表开发,看起来就是将数据按照指定格式的表格或图形呈现出来,这也是报表工具一直以来很擅长的环节。但是,原始数据经常并不适合直接呈现,需要先做一些复杂的处理,这就是数据准备环节。

从报表工具的眼光上看,数据准备属于报表之外的事情,可以堂而皇之地拒绝处理。但是,拒绝不等于不存在,这个工作总还要做。没有好的工具,目前报表的数据准备还处于比较原始的硬编码阶段,上千行的 SQL、几百 K 的存储过程和大量的 JAVA 代码充斥在报表之后。

落后的工具必然导致低下的生产效率,会严重拖累整个报表开发的进程,也就出现了前述的“挠头”现象。再由于大多数报表工具的不重视,这个问题迟迟还没有被解决的迹象。

报表数据准备之于报表有如树根之于大树,如果根本得不到解决,在枝叶上花多少精力都是白费。

这种深层次问题在报表开发初期一般很少碰到,但随着报表开发的深入这些问题会接连不断地出现了。

我们在这里收集整理了一批此类问题,以供报表开发者参考。内容比较多,建议先收藏再慢慢看。

目录

1. 零编码制作报表可能吗?

2. 当前报表开发的主要工作量在哪里?

3. 用存储过程和 JAVA 写报表数据源有什么弊端?

4. 报表的多样性数据源问题怎么解决?

5. 报表的性能问题出在哪些环节?

6. 海量数据的大报表有什么难点?

7. 报表开发为什么经常要中间表?

8. 报表热切换是什么意思?

9. 报表没完没了是个什么状况?

1. 零编码制作报表可能吗?

要回答这个问题,首先要明确啥程度算“零编码”?
以 Excel 为例,如果把写 Excel 公式(包括复杂一些的)看做零编码;而把写 Excel VBA 看做编码的话,

报表开发是可以零编码的!

但是,这有个前提:在数据(集)准备好的情况下才可以零编码!

为什么这么说?
我们知道报表开发主要分两个阶段:
第一阶段是为报表准备数据,也就是把原始数据通过 SQL/ 存储过程加工成数据集;
第二阶段是使用已准备的数据编写表达式做报表呈现。在报表工具提供的 IDE 里可视化地画出报表样式,然后再填入一些把数据和单元格绑定的表达式就可以完成报表呈现了,虽然表达式可能比较复杂,但相对硬编码要简单得多(Excel 公式和 VBA 的关系)。所以说这个阶段是能做到“零编码”的。

那报表数据准备怎么办?
很遗憾,这个阶段没法零编码,一直以来只能硬编码,想想我们报表里写的嵌套 SQL、存储过程、JAVA 程序就知道了。为什么报表工具发展这么多年报表呈现已经完全工具化而报表数据准备的手段还这样原始呢?因为这个阶段太复杂了,不仅涉及计算逻辑的算法实现,还涉及报表性能(要知道大部分报表性能问题都是数据准备阶段引起的)。

那报表数据准备是不是没办法了呢?
虽然不能做到零编码,但可以朝着简单化的方向努力,将数据准备阶段也工具化,这样可以使用工具提供的便利来简化报表数据准备阶段的工作,从而进一步简化报表的开发。

那怎么实现报表数据准备工具化?
要实现这个目标并不容易,像上面提到要考虑的内容有点多,大体来说数据准备工具至少要满足这几方面:
1. 具备完备的计算能力
说的有点拗口,掰开了其实在说既然在工具里做数据计算,那得让我什么都能算吧,不能原来 SQL/JAVA 写的放到这里就不行了,该有的计算方法和类库都应该有,最好用起来还比较简单(比原来硬编码难就没意义了),专业的说法叫:计算体系是完备的;

2. 支持热切换
这点是相对 JAVA 来说的,通过数据准备工具生成的算法应该是解释执行的,不能每次改完报表还要重启应用,即时修改即时生效;

3. 具备多源混算能力
通过数据准备工具可以同时连接多种数据源(RDBMS、NoSQL、TXT、Excel、Hadoop、HTTP、ES、Kafka 等等)进行计算,混合计算,这个数据源读个表、那个数据源加载个文件,两部分数据可以 join 到一起混算。现在我们的数据源太多了,报表常常会跨数据源取数,支持了异构源混算以后,原来还要考虑诸如数据是不是先入到一个库里的事情就不用管了,那叫一个清爽;

4. 高性能
直接简化数据准备的工作还不够,实现再简单跑不快也不行。所以,还要高性能,至少不能比原来跑的慢吧,大家都是讲道理的人;

以上是我们认为数据准备工具应该具备的能力,其他还有一些能力不是特别重要,但如果有最好了。包括:
* 有没有易用的编辑调试环境,可以很方便地调试算法;
* 为了更快能不能并行计算
* 有没有标准接口可以让其他程序或工具调用
等等,实际要用的时候照着这些特点去找就行了,有益无害。

说了这么多,总结来说,“零编码制作报表”的确更像一句口号,没法真正做到,但可以不断努力接近这个目标,求其上得其中嘛。

扩展阅读:

【数据蒋堂】第 43 期:报表开发的现状
报表提效资料汇总(体系结构和性能优化)

2. 当前报表开发的主要工作量在哪里?

我们知道,报表开发主要有两个阶段。
第一阶段:数据准备。将原始数据加工成报表需要的结果集(数据源);
第二阶段:数据呈现。根据已准备的结果集(数据源)编写表达式将数据以表格或图形方式呈现。

这两个阶段虽然处于同一报表开发过程,但实现方式却大不相同。

通常原始数据距离报表“能用”还相去甚远,通过 SQL/JAVA/ 存储过程等编码方式准备报表可用的数据源是第一阶段的目标,过程中可能涉及复杂的数据处理过程,因而这个阶段会牵扯较多的精力,占用的工作量也多。

数据源准备好后,通过报表工具来解决数据以何种方式(图表)、何种样式(外观)呈现的问题,通过点选设置、编写少量表达式就可以快速完成,实现简单,占用的工作量也少。

可以说,报表开发的工作量主要在数据处理(计算),数据处理后如何呈现,通过工具可以快速完成。

那是不是报表呈现阶段就不涉及数据处理了呢?
并不完全是这样。

早期报表开发
大概 2015 年以前,报表呈现方式主要以表格(或图表混合)为主,这时通过报表工具实现时就会涉及一定的数据计算,如分组汇总、多源分片、格间计算(同比环比)等。如下图所示:

计算分布在数据准备和数据呈现两个阶段,两个阶段的工作量相当(各占 50%)。这个时期,数据准备阶段可以把数据准备得“粗糙”一些,然后利用报表工具的计算能力在呈现阶段将数据进一步加工成目标结果进行呈现。比如,我们在数据准备阶段完成关联过滤,再在报表呈现模板中按多个维度分组汇总;或者在呈现模板中计算同比环比。

当前报表开发
随着报表工具的逐渐成熟,报表工具提供了更丰富的图形(类型和效果),报表选择图形呈现几乎与表格占比相当了(占比仍在增长)

使用图形呈现,由于没有“格子”,就无法利用报表工具的计算能力在呈现模板中完成数据处理,而图形本身并不具备计算能力(不包括硬编码),这时自然而然就要在数据准备阶段将前端需要的数据完全准备好,前端工具接收数据直接图形呈现。
图形设置基本没什么工作量,而后端数据源的准备工作就占了大头,数据呈现和数据准备所占的工作量占比约为 20%:80%。

当前报表开发的工作量主要在数据源端(数据准备阶段),要提升报表开发效率,势必要解决报表数据源准备效率问题。这也是为什么很多时候用上了一流的报表工具,但报表开发的工作量仍然很大的原因。

如何提升报表数据源端的开发效率,从而整体降低报表开发工作量?

借鉴报表开发的发展历史,或许可以获得一些思路。
早期,报表开发靠完全硬编码,无论数据准备还是报表呈现,后来报表工具出现替代了硬编码方式,将报表呈现阶段的开发工具化,解放了报表呈现阶段的人力(你可以感受一下写代码画报表比用工具做麻烦多少)。

按照这个思路,用工具替代硬编码就可以提高生产效率,将数据准备阶段工具化就可以解决报表数据源的开发效率,从而进一步提升报表整体开发效率。

数据准备工具可以是独立的,也可以包含在报表工具内部,后者要求报表工具的能力能够延伸到数据源层面。

扩展阅读:
【数据蒋堂】第 10 期:报表的数据计算层
如何应对报表开发中的复杂逻辑

3. 用存储过程和 JAVA 写报表数据源有什么弊端?

我们在报表开发中经常会使用存储过程准备数据,存储过程支持分步计算,可以实现非常复杂的计算逻辑,为报表开发带来便利。所以,报表开发中这样的存储过程并不少见:

3008 行,141KB 的存储过程,会给报表开发带来什么不好的影响?

1. 编辑调试性
存储过程难以编辑调试,这样几千行存储过程的开发周期往往要以周或月计,这样会严重影响报表的开发效率,而业务提的报表需求似乎都“很急”。

2. 维护性
相对开发的一次性,维护的工作可能要经常做。实际业务中报表经常会修改,这种现象叫做报表业务的稳定性差。报表的数据准备逻辑变化,修改上千行的存储过程对绝大多数报表开发人员来说都是噩梦。

有时这样的报表会分两拨人来做,DBA 或专业程序员负责编写存储过程给前端报表开发人员做报表,这样就避免了报表开发人员写存储过程。但这样报表修改的流程会变长,修改一张报表涉及多个人员之间沟通(还包括业务人员),如果负责报表前后端的两拨人隶属不同的团队就更麻烦了。

3. 知识传承
从维护性可以直接引出另一个“知识传承”的问题。还是拿上面的报表为例,如果一个新人要改上面的报表,你觉得他要多久能看懂存储过程,改完报表?

当然,这个问题还涉及很多管理方面的手段,单纯从技术本身来看,这样的报表想要很好地传承知识是很难的。

4. 安全性
对存储过程的修改需要较高的数据库权限,而报表经常要改就要经常操作数据库,这对数据库安全也是一个隐患,同样需要强管理机制才能保障一二。

5. 移植性
现在绝大多数规定禁止使用存储过程的原因,首当其冲的就是存储过程没有移植性。如果未来数据库发生变化需要迁移,不管将来是更换数据库类型,还是系统扩展(分表分库),大量无法移植的存储过程绝对是最头疼的问题。

当然,“换库”这件事情即使在今天仍然不会频繁发生,但是只要发生一次就够受了(有国产化或系统扩展预期的就要注意了)。

6. 耦合性
从维护性、安全性和移植性看来,存储过程会导致报表应用(前端)和数据库(后端)紧耦合。紧耦合除了会导致前面的三个问题外,还会让数据库编的臃肿,影响数据库性能。

重要的事情说好多遍,报表的业务不稳定,报表除了经常增加和修改,有时还会删除(不用了),而为这个报表准备的存储过程还在数据库里,这时想要删掉这个存储过程就比较难了。

为什么?

因为你不知道是不是还有其他程序在共用这个存储过程,删除会不会对其他程序产生影响。结果就是数据库的存储过程越积越多导致数据库臃肿,而有的存储过程还会涉及自动运行,虽然存储过程可能不再使用,但仍然在消耗数据库资源,长此以往数据库性能下降就成为必然了。

7. 多源支持
存储过程运行在封闭的数据库内,无法进行跨多数据源混合计算。关于多源问题,几年前在报表开发还不显著,那时大家都用关系库;但现在不一样了,同一个报表的数据可能来自多个不同类型的数据源(RDB/NoSQL/TxT/Excel/Hadoop/ES 等等),这时存储过程就无能为力了。

如何搞定这些问题?
有没有办法解决存储过程带来的这些问题呢?

当然有!

没有什么是硬编码解决不了的!用 JAVA 替代存储过程,脱离数据库运行来解决上面的问题(自行搜索 SOA 和微服务理念)。存储过程一个显示的好处是可以分步实现报表数据准备逻辑,这个优点 JAVA 也有,甚至比存储过程更彻底,说句文绉绉的话:JAVA 的离散性更好。

只是 JAVA 写起来比较麻烦,对于报表开发人员来讲太难了,如果还要加一个修饰词那就是太 XX 难了。存储过程使用的 SQL 语言非常适合做集合运算,分组汇总一句 group by 就写出来了,反观 JAVA 就不具备这个优点了,分组汇总可能要写上几十上百行才行(类库缺失会让开发复杂度急剧上升,想想你为什么不用汇编写程序而要用 JAVA?)。

JAVA 还有一些其他的问题也不容忽视。

不支持热切换
JAVA 还有一个非常致命的缺点,就是不支持热切换。报表经常要改(又来一遍),修改报表数据源以后还要重新编译、重启应用才能生效,对绝大多数业务系统都是不能接受的。报表讲究的不仅是查询立等可取,修改也要实时生效才行。

报表与应用紧耦合
与使用存储过程会导致报表与数据库紧耦合类似,用 JAVA 准备报表数据源会导致报表模块和应用的其他业务模块紧耦合不宜维护。

我们知道,报表大多数情况都是作为一个模块集成到应用系统提供报表查询服务,集成的方式可以是 API(jar 包)方式紧集成;也可以将报表单独发布成服务,通过服务调用的方式松集成,这样报表服务器产生的任何压力或问题都不会影响应用系统(高可用)。*API 紧集成后,由于报表数据源是 JAVA 写的,这样就要和主应用的代码一起打包,无法作为独立的模块维护,而未来想要拆分也基本不可能了;* 服务松集成则完全无法实现。

所以,用 JAVA 写报表数据源虽然可行,但也不是特别理想。

那有没有其他办法呢?

我们比较一下存储过程和 JAVA 的优缺点可以发现,解决问题应该是沿着继承二者优点,改进缺点的方向进行。清晰起见,总结一下需要的点。

把主要的点列了一下,我们的目标就是找到支持这些点的技术手段(问号所在行)。

易开发、易维护
这注定了这些能力应该是报表工具内置的,这样报表开发人员自己就能使用工具搞定报表开发,而不必依赖其他人或团队;

热切换
热切换要求这个技术应该是解释执行的,这样才能做到实时修改实时生效;

支持多源
能够对接多种不同类型的数据源进行混合计算,比如文本和数据库表的 join;

低耦合、可移植
数据准备能力和报表呈现能力都报表内置了,自然与应用和数据源都解耦了,未来系统扩展自然毫无压力。

这样我们可以很容易想到在报表端增加一个计算模块(或引入其他计算工具),来替代存储过程或 JAVA 为报表准备数据,这个模块可以是由嵌入报表工具的脚本来实现,结构可以是这样的

脚本要具备完善的计算能力(什么计算都能算),支持多源,解释执行允许热切换这些能力。

这才是报表工具的未来!

扩展阅读:
怎样减少报表开发中对存储过程的依赖
如何应对报表开发中的复杂逻辑

4. 报表的多样性数据源问题怎么解决?

在报表开发早期,报表连接的数据源基本只有关系数据库,而且经常只有一种或者只有一个数据库。

但今天就不一样了,数据源种类繁多,除了 RDBMS 还有
1.MongoDB、Cassandra、HBase、Redis 这些 NoSQL 数据库;
2.TXT/CSV、Excel、JSON/XML 等文件;3.HDFS 等分布式文件系统;
4.webService;
5.ES、Kafka 等其他数据源形式
……

当这些都成为报表数据源,报表需要从这些数据源分别或混合取数运算进行报表呈现时,报表就出现了多样性数据源问题。

具体是什么样的问题呢?

主要是两个问题,复杂计算和多源关联计算。

1. 复杂计算
我们知道,报表中的计算主要集中在两处:

一处是数据准备阶段。
通过 SQL/ 存储过程 /Java 程序为报表准备数据,这个阶段可能涉及非常复杂的数据处理逻辑。这样, 计算能力尤其是集合计算能力较强的 SQL 就比较擅长了,通过 SQL、复杂 SQL 可以完成大部分的报表数据准备任务,有些涉及较多业务逻辑的计算还可以使用存储过程,万不得已时用 JAVA 自定义数据源完成。

这是早期基于单一 RDBMS 开发报表时数据准备的常用手段,主要依靠 RDBMS(SQL)的计算能力来实现。

但这种方式在多样性数据源的场景下就行不通了,因为有的数据源根本就不支持 SQL,甚至计算能力都比较弱(如 NoSQL),或者根本就没有计算能力(如文本),这样,数据准备计算计算无法在这个阶段实现,就要看另外一处是否可以完成了?

二处是报表呈现阶段
根据第一阶段已准备的数据,在报表模板中填入绑定格子的报表表达式或图形来呈现报表是使用报表工具开发报表的常用方式。这说明报表工具具备一定的计算能力,通过表达式可以实现分组汇总、过滤、排序,复杂一些的同比环比等计算。

但是,报表工具的计算能力是有限的。不考虑性能的情况下,单纯从数据源中读取数据到报表呈现阶段完成数据组织和报表呈现很多时候是做不到的,这也是为什么我们会在数据准备阶段进行复杂数据处理的原因了。

2. 多源关联计算
与基于单一的某个数据源进行的复杂计算不同,有时一个报表会同时连接多个数据源,多个数据源之间要混合计算,比如 MongoDB 的数据和 RDBMS 的数据关联运算,文本和 Excel 关联运算等。

这种跨异构数据源的关联无法直接通过数据源自身的能力实现,只能借助其他方法。


那报表的多样性数据源问题如何解决呢?

方法总比问题多。目前大家普遍采用两种方式来解决报表多样性数据源的问题。

借助 RDBMS
曲线救国。将多样性数据源的数据通过 ETL 灌到关系库中,再基于关系库出报表,这样就可以避免多样性数据源的问题,转而使用最熟悉的手段来解决。

不过,这种方式的局限性很大。因为之所以出现多样性数据源,是因为各种数据源有各自适用的场景,换句话说很多是关系库搞不定的,所以才会用这些数据源,比如 NoSQL 的 IO 吞吐能力很强,但计算能力较弱;文本 /Excel 文件适合做临时存储且不需要持久化到 DB;Webservice 则非常灵活,入库的动作就显得过于笨重,…

且不说多样性数据源的数据是否能转换到关系库中,由于要经过 ETL 的过程,数据的实时性如何保证?数据量较大时除了 ETL 慢,RDBMS 的容量是否够用?查询性能是否满足报表查询要求?等等这些问题都是这种方式要面对的。

采用这种方式经常是“不得已”,因为解决某类问题上了其他数据源,结果因为出报表又要用回关系库,也不知道隐含了多少辛酸。

JAVA 硬编码
通过 RDBMS 来解决报表多样性数据源的问题有这样那样的问题,那直接硬编码怎么样?通过 JAVA 硬编码对接多样性数据源为报表准备数据,毕竟硬编码想干啥就干啥。

这种方式我们之前有分析过,除了编码难、维护难的问题(报表开发人员基本搞不定),还存在无法热切换(JAVA 是编译型语言)和与业务应用紧耦合(代码要跟业务应用主程序一起打包部署)这些问题。这是我们之前聊过的: 用存储过程和 JAVA 写报表数据源有什么弊端?

硬编码似乎也不理想。

事实上,我们只需要增强报表工具的计算能力就能解决这个问题。

1. 首先,提供多样性数据源的支持,通过报表工具可以连接这些数据源,要实现这一步相对简单;

2. 其次,提供复杂计算支持,让所有的复杂计算都能在报表中完成。实现手段可以是强化呈现端的计算能力,通过报表格子表达式就能完成这些复杂计算。不过,对于绑定格子的计算(状态式计算)想要支持复杂计算并不容易,在呈现端要兼顾数据处理和数据呈现很多计算就做不了了,而且呈现格会带有很多呈现属性(字体、颜色、边框等等),带着这些属性计算会占用过多内存,严重影响计算性能。


很难在报表呈现格表达式中完成复杂计算(功能和性能都不满足)

可以想到的另外一种方式是在报表中增加计算模块用来专门做多样性数据源混合计算,其位置与原来为报表准备数据的 SQL 和 JAVA 相当,只不过是内嵌在报表中,属于报表自身的能力,结构大概是这样

在原有的基础上增加了计算模块,计算模块可以通过可编程计算脚本实现,但对这个脚本能力有要求:

1. 提供多样性数据源访问接口
能直接对接多样性数据源是基础,种类越丰富对多样性数据源问题解决得越充分;

2. 支持复杂计算
当数据源自身不具备强计算能力时,通过脚本可以完成报表数据准备阶段的复杂计算逻辑,脚本提供丰富的计算类库,可以很方便地实现这类计算,最好比 SQL 和 JAVA 都简单;

3. 支持多源关联
可以跨异构数据源关联计算,这是也解决报表多样性数据源问题需要具备的重要能力;

4. 支持热切换
脚本修改后可以实时生效,而不会像 JAVA 一样需要重启应用。

当我们遇到报表多样性数据源问题需要选择报表开发技术时不妨沿着这个方向考量一下。

扩展阅读:
【数据蒋堂】第 10 期:报表的数据计算层
在报表中直接使用多样性数据源

5. 报表的性能问题出在哪些环节?

报表性能是总也避不开的话题,报表作为 OLAP(在线联机分析)中的主要应用场景,无论从涉及数据的宽度(表数量),还是数据的广度(查询范围)都可能非常巨大;而且在报表中还经常伴随非常复杂的数据处理逻辑,这些都会影响报表的运行速度。而服务器环境、数据库环境、JDBC 效率、网络环境、客户端环境这些也都都跟报表性能密切相关。

报表性能可能跟很多因素有关,非常复杂。这里我们试着从报表运行的各个阶段来分析报表性能问题产生的主要原因及其应对方法。未尽之处,欢迎讨论。

我们知道报表运行主要分报表解析、数据准备、数据传输、报表计算和报表生成 5 个阶段。除了报表解析是引擎加载解析模板,还未开始运算外,其他 4 个阶段(示意图中黄色的部分)均可能引起性能问题。

我们在分析报表性能问题时一定要先定位是哪个阶段引起的,抓主要矛盾。定位的方法也很简单,就是分析报表运行日志,很多报表工具都会输出各个阶段的运算时间,看看哪个阶段耗时最长,就是问题发生的主要阶段了。

1. 数据准备
报表数据准备是指从数据源中读取数据并将数据组织成报表可用的结果集的过程。在报表中往往是以数据集的形式存在,可以通过 SQL、存储过程或 JAVA 实现。

数据准备并不是简单地将数据源的原始数据取出就结束了,而是会伴随一些计算过程,有些还很复杂,可以想想一下平时我们开发报表时编写的 SQL 绝大多数情况下都不是简单的 select * from tbl。如果这个 SQL 比较复杂(由于大多数情况都是使用 SQL 准备数据,所以这里我们仅以 SQL 为例,其他数据源可以参考 SQL 数据集的解决办法)运行较慢,就会导致报表变慢。

这个阶段在数据库中运行,本质上跑的快慢都跟报表无关,但好的报表工具是可以干预优化这个过程的(后面再说)。

数据准备阶段慢,可以先试着优化 SQL,但 SQL 优化往往复杂度较高,所以也经常采用预计算的方式进行性能优化(这里不讨论硬件升级、数据库扩容等物理优化手段)。

(1) 优化 SQL
我们知道复杂 SQL(关联多、嵌套多)是比较难优化的。数据库的透明化机制让写 SQL 时不用关心底层的执行顺序,由数据库优化器自动执行,这样可以简化编写 SQL 的难度,但过于透明的机制让我们很难干预 SQL 的执行路径,也就很难优化 SQL 了。

(2) 预计算
SQL 无法优化或优化效果不理想时,通过预计算可以提升报表数据准备效率。将报表需要的结果集事先加工出来存储到中间表中,报表查询时直接读取加工好的结果集,这样就可以节省大量计算时间,从而提升报表性能。预计算本质是用空间换时间的手段。

不过,预计算的缺点也非常明显,你不妨先思考一下有哪些问题?

主要的问题有两个:时间问题和空间问题。

时间问题
预计算需要“事先”加工,其本质是一个 ETL 过程,这样就会引起两个时间问题,数据的实时性和预计算的时间成本。

报表基于预计算的结果查询只能查询预计算时点以前的数据,无法查询预计算到当前时间的数据,这样就导致了数据时效性差,不适合数据实时性要求高的业务场景(比如交易系统)。

预计算往往会放到业务空闲的阶段进行,比如前一天夜里到第二天上班前,通常还要预留一些时间容错(跑失败了要重新跑),这样可能留给预计算的时间也就 5、6 个小时,这个时长基本是固定不变的,而数据规模、业务复杂度、报表数量都会不断增长,未来极有可能会引发预计算跑不完而影响业务使用的问题。

空间问题
预计算的结果是要落地的,往往会存储在数据库物理表中,这样的表多了会占用大量数据库空间,引起数据库容量问题;另外这样的表多了还会带来管理上的问题。

预计算的应用范围很窄,而 SQL 又比较难优化,还有什么其他优化手段吗?

(3) 借助其他高性能计算引擎
在数据库内搞不定报表数据准备阶段优化时可以引入其他高性能计算引擎,这在业内并不少见,但几乎所有技术都是采用独立存储 + 分布式架构来输出高性能的,这对报表应用架构(主要是数据库)的改变就有些大了,难度很大。

比较简单直接的方式是在报表内部就能提供改变 SQL 执行路径的手段,并且可以改善那些显著低效的 SQL 算法(比如 topN),这样就可以在不改变应用架构的情况下实现数据准备阶段的优化。

这就要求报表工具提供数据准备阶段的计算能力,既可以分步执行 SQL(指定执行顺序),还可以改善算法效率。其表现形式可以是一种内置在报表工具中的计算脚本,脚本具备分步计算和强计算能力。


报表工具计算模块提供可干预 SQL 执行路径能力和高效的强计算能力


2. 数据传输
报表通过 JDBC 接口访问数据库读取所需数据时,如果数据量比较大或者数据库 JDBC 性能较差(要知道各种数据库的 JDBC 效率是不同的)会导致数据传输时间过长,导致报表变慢。

由于我们没法改变数据库的驱动,我们只能在报表层面想办法。一个可行的办法是通过并行取数来提速。

(1) 并行取数
通过建立多个数据库连接(这时要求数据库相对空闲),采用多线程的方式同时读取报表所需数据,可能是同一个表,也可能是多个表关联计算后的结果,这样数据传输的时间理论上就会缩短到原来的 1/n(n 是线程数),从而提升报表性能。

那么这种并行取数实现起来难度如何呢?

因为目前大部分报表工具不支持并行取数,要想通过并行来加速数据传输只能自己使用 JAVA 硬编码来做。我们曾经讨论过在报表中使用 JAVA 硬编码准备数据的弊端,而编写并行程序难度又提升了一级,要知道,并行以后还要归并(merge)各个线程的计算结果,merge 不是简单地纵向拼接就完了,有时还涉及分组和顺序。

使用支持并行取数的报表工具
比较简单有效的方式是使用支持并行取数的报表工具,报表开发人员不需要考虑数据资源冲突、结果归并等复杂问题就可以简单实现。

(2) 异步传输
当数据量较大时,可以通过异步机制将数据分批返回给报表,报表接收部分数据后就立刻呈现,后台同时进行不间断的数据传输,这样就可以提升报表的呈现速度。

异步传输需要考虑很多方面,不建议自己硬编码实现,最好使用支持异步传输功能的报表工具,简单快捷。

3. 报表计算
数据传输完成后,报表引擎会根据已准备数据和报表表达式运算报表,这个阶段也是非常容易出现性能问题的。

性能问题常见于多数据集报表。

现在很多报表工具都提供了多数据集能力,允许开发者在一个报表中建立多个数据集,这样可以分别组织数据,尤其当数据来自不同数据库时,多数据集尤其方便。但这种方式为报表开发带来便利的同时却会带来很严重的性能问题。

我们知道,多数据集的关联是在报表单元格的表达式中完成的,类似这样 ds2.select(ID==ds1.ID),报表引擎在解析这个表达式时会按照顺序遍历的方式完成关联,即从 ds2 中拿出一条记录,到 ds1 中遍历,查找 ID 相同的记录;然后再拿第二条再去遍历查找;…
这个运算复杂度是平方级的,数据量小的时候没什么影响,数据量稍大时性能就会急剧下降。

另外,报表单元格还带有大量呈现属性(颜色、字体、边框等),带着这些属性运算也会拖慢报表的运行速度。所以,对于数据量较大的多个数据集,最好在数据准备阶段使用更高效的关联算法完成关联。

在数据准备阶段完成关联

将多数据集关联转移到数据准备阶段完成,通过 SQL 就可以实施更加高效的关联,只是 SQL 只能基于单一数据库操作,如果数据来自多个数据源时就不灵了。这时,最好报表工具在数据准备阶段还提供诸如 HASH JOIN 等高性能关联算法,以便满足异构数据来源时的关联运算。HASH JOIN 算法可以整体地看待几个数据集,效率比报表工具采用的过滤式关联要高得多,几千行规模时几乎是零等待。

4. 报表生成
报表计算完成后会生成引擎会将计算结果以 HTML 的形式输出,生成最终要呈现报表。这个阶段的性能问题主要有两方面。

(1) 页面渲染慢
当报表中添加了大量的呈现效果(隔行异色、背景图、警戒色等)时,页面渲染的速度就会变慢。减少过多的报表效果使用可以提升渲染速度;如果这些效果是必须的,那就要比拼使用的报表工具的能力了。

另外,如果报表单页的内容过大也会影响页面呈现的速度。对于比较高或者比较宽的报表呈现时建议采用分页方式输出,从而提升页面呈现效率。

(2)客户端环境差
总体上是跟用户端环境有关系,比如网络问题、设备问题等,遇到时需要具体分析。

通过了解报表运行各阶段可能出现的性能问题,以及对应解决办法,有些我们可以硬编码或借助其他技术来解决,有些还需要报表自身来提升。最直接的方式是使用能够提供这些能力的报表工具,这个工具大概要具备这些特性才能帮助我们有效解决报表性能问题。

1. 支持分步
这样可以有效优化 SQL 执行路径,提升数据准备效率;

2. 内置强计算能力和高效算法
能够改善原有低效的算法,提高数据计算效率;
支持 HASH JOIN 等高性能关联算法,以减少在呈现模板中进行多数据集关联;

3. 支持并行取数
能够通过多线程并行方式提升数据传输速度效率;

4. 支持异步读取
提供异步取数异步呈现机制,加速大数据量报表的呈现效率;

5. 支持多数据源
提供多样性数据源接口,并能够进行多数据源关联运算(同 2)。

扩展阅读:
【数据蒋堂】第 3 期:功夫都在报表外 - 漫谈报表性能优化
性能优化是个手艺活
如何分析报表性能问题

6. 海量数据的大报表有什么难点?

实际业务中有些报表比较“大”,查询出的报表数据行数可以达到几千万甚至上亿,这类行数很多的报表通常被成为“大报表”。大报表大部分情况下是清单明细报表,少量是分组报表。

大报表查询通常不会采用一次性取出所有记录再交给前端呈现的方式,因为这样要等很久,用户体验极差;而且报表服务器内存也吃不消。

常见的方式是通过分页来呈现大报表,一次只取一小部分数据,取数结束后立刻交给前端呈现,当页码变化时再取出相应页数的数据,这样可以加快报表呈现速度,用户几乎没有等待感。

具体如何实现呢?有几种方式。

1. 数据库分页
业界最常用的做法是使用数据库分页来实现,具体来讲,就是利用数据库提供的返回指定行号范围内记录的语法。界面端根据当前页号计算出行号范围(每页显示固定行数)作为参数拼入 SQL 中,数据库就会只返回当前页的记录,从而实现分页呈现的效果。

主要借助关系数据库自身的能力,每种数据库实现上会有所差异,Oracle 可以使用 rownum,mysql 则可以 limit,具体实现网上有很多资料这里不再赘述。

数据库分页有没有什么不足?任何技术都有其应用范围,数据分页的问题主要集中在以下 4 点。

(1)翻页时效率较差
用这种办法呈现第一页一般都会比较快,但向后翻页时,所使用的取数 SQL 会被再次执行,并且将前面页涉及的记录跳过。对于有些没有 OFFSET 关键字的数据库,就只能由界面端自行跳过这些数据(取出后丢弃),而像 ORACLE 还需要用子查询产生一个序号才能再用序号做过滤。这些动作都会降低效率,浪费时间,前几页还感觉不明显,但如果页号比较大时,翻页就会有等待感了。

(2) 可能出现数据不一致
用这种办法翻页,每次按页取数时都需要独立地发出 SQL。这样,如果在两页取数之间又有了插入、删除动作,那么取的数反映的是最新的数据情况,很可能和原来的页号匹配不上。例如,每页 20 行,在第 1 页取出后,用户还没有翻第 2 页前,第 1 页包含的 20 行记录中被删除了 1 行,那么用户翻页时取出的第 2 页的第 1 行实际上是删除操作前的第 22 行记录,而原来的第 21 行实际上落到第 1 页去了,如果要看,还要翻回第 1 页才能看到。如果还要基于取出的数据做汇总统计,那就会出现错误、不一致的结果。

为了克服这两个问题,有时候我们还会用另一种方法,用 SQL 游标从数据库中取数,在取出一页呈现后,但并不终止这个游标,在翻下一页的时候再继续取数。这种方法能有效地克服上述两个问题,翻页效率较高,而且不会发生不一致的情况。不过,绝大多数的数据库游标只能单向从前往后取数,表现在界面上就只能向后翻页了,这一点很难向业务用户交代,所以很少用这种办法。

当然,我们也可以结合这两种办法,向后翻页时用游标,一旦需要向前翻页,就重新执行取数 SQL。这样会比每次分页都重新取数的体验好一些,但并没有在根本上解决问题。

(3) 无法实现分组报表
除了清单报表,有时我们还要呈现大数据量的分组报表,报表包含分组、分组汇总及分组明细数据。我们知道,按页取数按照翻页每次读取固定条数(一页或几页)记录,这样根本无法保证一次性读取一个完整分组,而分组呈现、分组汇总都要求基于整组数据来操作,否则就会出错。

(4) 无法使用其他数据源
数据库分页是借助关系数据库自身的能力,而对于非关系数据库就不灵了。试想一下,NoSQL 怎么用 SQL 分页,文本怎么做分页?

报表的多样性数据源话题我们曾经讨论过,在数据规模迅速膨胀的今天,基于非关系数据库出报表已经非常普遍。


除了数据库分页,还有其他更好的方式吗?

2. 硬编码实现
我们发现基于数据库的分页方式强依赖数据源,无法满足其他数据源类型的需要,也就是与数据库紧耦合的。我们需要一个低耦合数据源的实现方式以应对多样性数据源场景,还能同时解决效率、准确性和分组呈现问题。

按照主流的解题思路,可以通过编码方式实现这个目标,实现思路大概是这样:

基于数据库时,
把取数和呈现做现两个异步线程,取数线程发出 SQL 后就不断取出数据后缓存到本地存储中,呈现线程根据页数计算出行数到本地缓存中去获取数据显示。这样,只要已经取过的数据就能快速呈现,不会有等待感,还没取到的数据需要等待一下也是正常可理解的;而取数线程只涉及一句 SQL,在数据库中是同一个事务,也不会有不一致的问题。这样,两个问题都能得到解决。不过这需要设计一种可以按行号随机访问记录的存储格式,不然要靠遍历把记录数出来,那反应仍然会很迟钝。

基于非数据库时,
同样设置取数和呈现两个异步线程,取数时通过文件游标(或其他数据源提供的分批取数接口)分批读取数据并缓存到本地,呈现阶段则与上述方式完全一致。

然后再利用报表工具开放的接口和前端报表进行交互,完成报表的分页呈现。

做分组报表时,
就需要一次性取出完整分组,在代码里进行分组汇总,并将汇总结果插入结果集一并返回给前端报表进行呈现,同时还要设置分组记录标志位,以便前端报表在呈现时能够为分组行设置不同的显示效果(如加粗、标红)。

实现后的效果类似下面这样:
gif

取数线程不停地取数缓存,呈现线程从缓存中读取数据呈现,总页数不断变化

通过上面的描述,可以看到自己硬编码虽然可以实现,但复杂度很高。除了多线程编程,还要考虑缓存数据存储形式、文件游标、分组读取与汇总,此外借助其他报表工具进行呈现时还要对方开放足够灵活的接口,…,这些都是挑战。

而且我们只考虑了呈现,如果还要导出 Excel 怎么办?如果还要打印怎么办?毕竟报表既然能查就应该能导出,能打印。这些需求在金融、制造行业都是真实存在的。


3 使用支持大报表的报表工具
如果前端使用报表工具开发报表,选用一个直接支持大报表呈现、导出、打印的报表工具则更为直接。工具实现了两个异步线程的大报表机制,同时解决上面我们提到的问题,这些方面都封装好直接使用。

大数据时代要支持大报表,这应该是报表工具必备能力。

扩展阅读:
大清单报表应当怎么做?
海量清单与分组报表的实现
如何实现海量数据清单和分组报表
秒级展现的百万级大清单报表怎么做

7. 报表开发为什么经常要中间表?

在数据库中有一类用于保存中间计算结果的物理表,通常被称为“中间表”。中间表主要跟 OLAP(在线联机分析)业务有关,产生的原因主要有以下几方面。

中间表来源

1. 计算逻辑复杂
在 OLAP(报表或查询)业务中,有些计算逻辑很复杂,每次都从头写会导致报表开发过于繁琐,而且有的计算用 SQL 很难写出来。这时会采用中间表事先计算好,再基于预计算的中间结果开发报表。

计算逻辑复杂常见于报表业务中,以固定报表最为常见;多维分析则比较少见。

2. 查询性能差
当查询涉及的数据量很大或者计算逻辑很复杂时查询性能会很差。为了提升查询性能,增强用户体验,通常会把汇总结果实现计算出来存储在中间表中。基于预汇总的中间表查询速度会快很多。

在实际业务中,大部分提升查询速度的中间表也都是为报表服务的。

3.ETL 过程转存
ETL 过程也会产生中间表。ETL 过程中常常会涉及到数据库的数据,正常的 ETL 过程应当是 E、T、L 这三个步骤逐步进行,也就是先清洗转换之后再加载进数据库,最后在数据库中的只是合理的结果数据。但是,E(清洗)和 T(转换)这两个步骤中会涉及到大量数据计算,而在数据库外实施这些计算很不方便,所以实际情况就会是把涉及到的所有数据都先加载进来然后再进行清洗和转换,ETL 过程变成了 ELT 甚至 LET。事先要加载的这些数据在关系数据库中也必须以表的形式存储,这就使数据库中增加了许多并非最终需要的中间表。

如果观察一下这些跑批任务,你会发现 ETL 任务很多都是为报表业务服务的。

4. 多样性数据源混合计算
另一种情况是多样性数据源造成的,这也是为数据呈现(报表查询)服务的。现代应用中的数据呈现经常会涉及数据库外的数据,目前一般的做法是把库外数据定时导入到数据库中,然后就能和数据库内的数据一起运算产生报表,否则很难实现数据库内外的数据的混合运算。这当然也会让数据库中多了一些表,而且,有些互联网上取过来的数据常常是多层的 json 或 XML 格式,在关系数据库中还要建立多个关联的表来存储,会进一步加剧中间表过多的问题。

通过列举的 4 个中间表产生的主要原因,我们发现一个共同点:中间表大部分情况都是为报表服务的。我们也知道,实际业务中的报表数量非常多,而且报表业务业务不稳定经常会新增修改报表,这会导致中间表数量不断增多。

中间表会带来哪些问题

中间表是一把双刃剑,提供很多便利的同时也会带来一些问题。

我们曾在一个运营商的报表系统中,发现了一个让人吃惊的现象。在 DB2 数据仓库中,有两万多个数据库表!经过深入了解发现,真正的原始数据表只有几百张,剩下的大量的数据库表都是为查询和报表服务的中间表。

像这种系统经过几年乃至十几年的运行,数据库中的中间表越来越多,甚至出现这种上万个的情况并不少见。大量中间表带来的直接困扰是数据库存储空间不够用,面临频繁的扩容需求。中间表对应的存储过程、触发器等等需要占用数据库的计算资源,也会造成数据库的扩容压力。

中间表过多还会带来管理方面的问题,对于成千上万张中间表想要梳理清楚恐怕是一件非常头疼的事情。

那么,是不是可以清理掉一些不用的中间表?一般的结论都是:搞不动。数据库中的中间表是不同程序员制作的,有的是综合查询系统使用,有的是报表系统使用。中间表之间还存在交叉引用,有些程序员看到有别人生成的中间表就直接使用了。有时候一些查询报表已经废弃不用了,但是对应的中间表没人敢删,因为不知道删掉之后会影响其他什么查询或者报表。

很多情况下,项目组只好为了越来越多的中间表去扩容数据库。但是数据库的扩容成本太昂贵了:不管是换更强的服务器(纵向扩容),还是增加数据库服务器的节点(横向扩容),都不便宜。

总结来说,中间表会带来管理、容量和性能三方面的问题。

如何解决中间表的问题?

可以很容易想到的方式是使用库外文件存储中间表数据,这样中间表脱离了数据库就不会对数据库再产生影响。但是,在实际应用中这种办法却很少使用。为什么呢?

我们知道,中间表是要再计算的,基于中间表查询的报表还要进行数据过滤,有的还要再次汇总,还可能涉及关联计算,这些操作在数据库里通过 SQL 完成很简单。但是文件没有计算能力,要实施这些计算只能硬编码,用 JAVA 来做,使用 JAVA 来做集合运算又非常麻烦,远没有数据库(SQL)方便。所以采用文件存储中间表的方式使用并不广泛,主要是由实现复杂度过高导致的。

那还有什么好的方式呢?

使用支持文件源的报表工具
既然中间表大部分是为报表服务的,而通过将中间表外置到文件中可以解决中间表带来的这些问题,那么直接使用支持文件源的报表工具是否就可以了呢?

答案是肯定的。

我们来看一下要实现这个目标,报表工具要具备哪些能力?

(1) 丰富的计算类库
要解决文本计算难的问题,报表工具要提供丰富的计算类库,除了能完成所有数据处理任务(都能算)以外,还要实现简单,这是基础,太复杂了没法用;

(2) 多样性数据源接口
支持多样性数据源以后,就可以不用通过数据库中转直接读取多样性数据源数据出报表;

(3) 异构数据源混合计算能力
提供多样性数据源接口后,还要能够进行异构源间的混合计算,这样就可以彻底解决掉多样性数据源带来的中间表问题;

(4) 高性能
除了实现简单以外,计算性能也要有保障,从而满足前端报表查询的性能需要。

具体实现上可以在报表工具中增加计算模块来对接“库外中间表”,结构类似这样

其中库外中间数据文件可以采用本地文件,也可以使用网络或分布式文件系统,或其他数据源。

要解决中间表问题,需要报表工具强化自身的计算能力才能实现!

扩展阅读:
【数据蒋堂】第 14 期:计算封闭性导致臃肿的数据库
【数据蒋堂】第 15 期:开放的计算能力为数据库瘦身
有效减少数据库中间表的报表开发方法
为什么会有这么多中间表?

8. 报表热切换是什么意思?

热切换(Hot Swap)是指在系统不停机的情况下更换系统部件,在报表业务中则是指在不重启报表及相关应用的情况下完成对报表的维护(新增、修改、删除),冷切换则恰好相反。

报表业务很不稳定,业务开展的过程中会刺激出更多查询统计需求,如果每次需求实现后都要等系统空闲(往往是非工作时间)时重启系统才能让修改后的报表生效,那将会对业务使用造成非常大影响(延迟)。因此在需求经常会变化的报表业务中,热切换显得尤其重要。

事实上,在报表业务中要实现热切换并不难,只要在开发报表时选择合适的技术手段,避免使用诸如 JAVA 这样的编译型语言(虽然重写类加载器可以变相实现热加载,但过于繁琐,也没从根本上解决问题)即可。

但在实际应用中我们却看到大量使用 JAVA 来开发报表,造成无法热切换的情况。为什么会这样?

报表业务有两个特点,一个是报表业务很复杂。报表是一项负责综合性统计的任务,将原始数据加工成业务需要的报表形式要走很长的路,不仅数据处理逻辑繁琐,报表呈现样式也千奇百怪,所谓中国式复杂报表。

另一个特点是嵌入使用。报表经常会作为应用系统的一部分(模块)嵌入到系统中使用。

现在报表呈现格式方面的问题已经基本得到解决,以国内成熟报表工具为主导的一批国产软件可以很好地解决中国式复杂报表呈现的问题,而且这些工具大多数采用解释执行机制,报表模板修改后可以实时生效。

但数据准备阶段的问题却一直没有解决,复杂的数据处理和业务逻辑控制让开发人员不得采用硬编码手段来实现。由于大部分应用系统都采用 JAVA 开发,既然报表是系统的一部分,业务逻辑又这么复杂,硬编码的首选自然是 JAVA 了。这是导致在报表中大量使用 JAVA 导致无法热切换的原因。

那么如何解决这个问题?

将报表的数据准备也工具化!

早期,报表开发完全靠硬编码的时代,大家也普遍采用 JAVA 来“画”报表,这样势必存在无法热切换的问题,后来报表工具出现解决了呈现模板的热切换问题。所以,要彻底实现报表热切换,将数据准备也工具化是一个可行的方向。

然而,报表的数据准备和报表呈现两个阶段分别使用不同工具会造成报表开发和维护上的困扰,因此,最好的方式是增强报表工具自身的计算能力,将两个阶段都融入可以解释执行的报表模板中,从而彻底实现报表热切换。

我们知道,报表工具本身具备一定的计算能力,在呈现模板的单元格中可以编写分组、求和、比值等表达式,但由于格子的限制很难处理复杂的数据逻辑,这也是为什么在报表中需要数据集(JAVA/SQL)来准备数据的原因。

一个可行的办法是在报表工具中增加可解释执行的计算脚本(模块)用于数据准备,这样在计算模块中可以实现任意复杂的数据处理逻辑,同时解释执行的脚本内置在报表模板中实现热切换。

报表计算模块(脚本)要具备这样一些能力:1. 解释执行
这是实现热切换的基本能力,数据准备和报表呈现内置在一个报表模板中,采用解释执行机制运行;

2. 计算体系完善
能实现所有的数据处理任务,原来在 JAVA 中的报表数据准备计算在脚本中都应该能完成,具备丰富的集合运算类库;

3. 易开发维护
实现上要足够简单,这样报表开发人员就能直接搞定数据准备和报表呈现,而不再依赖其他专业程序员;同时在一个报表模板里更易于维护。

参考资料:
如何应对报表开发中的复杂逻辑

9. 报表没完没了是个什么状况?

可以先想一下自己的部门或项目组是否面临这些问题:
1. 投入很多技术力量做报表,却还是疲于应付
2. 用了高端报表工具和敏捷 BI,却还是不够用
3. 技术高手用来做报表,感觉很浪费 4. 对于频繁多变的报表需求,需要低成本应对方案

专门用于统计分析的报表业务有一个特点,就是业务稳定性非常差。在业务开展过程中会催生很多新的查询需求,而且已实现的查询需求还会经常变化,这就造成了没完没了的报表。所以经常会有这么一段对话

报表没完没了是需求使然,无法规避,只能适应,而目前主要的是问题是普遍缺少一种低成本的方案来适应没完没了的报表。

为什么报表开发成本一直居高不下?

我们知道,报表工具的主要作用是将报表呈现阶段的开发工具化,使用报表工具可以将原本需要硬编码的工作通过工具来提高生产效率;但报表开发的另一个阶段:数据准备,却仍然在使用原始的硬编码方式处理,有时我们要编写非常复杂的 SQL、存储过程和 JAVA 程序,这样的工作通常要依赖高水平的技术人员才能完成。

另外,采用过于原始手段会带来报表维护困难,复杂的代码无论从编写还是修改都比较困难,这样又会加剧报表开发成本居高不下!

除了技术原因外,还有应用结构和团队管理方面的因素也会造成报表开发的成本居高不下。在许多应用系统中,报表是耦合在其中的一些功能,而业务系统的技术环境很复杂,这就迫使报表开发人员也要熟悉这些东西,也就很难把报表开发人员和业务系统的开发人员分开,业务系统上线之后,开发人员仍然要继续坚守开发报表,很难把这项工作转交给客户方的运维人员。

开发过程中的有效沟通也会影响工作量。我们经常会发现这样的现象:业务人员提出报表需求,等技术人员做好后才发现某些概念术语的理解有误,统计口径不一致,结果并不是业务人员想要的,然后也只能重做,甚至反复多次才能正确实现,严重浪费开发资源。


如何解决报表开发成本过高?如何低成本地应对没完没了的报表?

可以尝试通过如下五个步骤来解决这些问题。

1. 引入报表工具

先把最容易解决的问题解决掉,通过引入专业的报表工具解放报表数据呈现阶段的人力,报表工具可以完成包括中国式复杂报表在内的各种图表呈现。选择成熟且性价比高的工具是这个阶段的主要目标。

2. 增加计算模块

引入报表工具后,解放了数据呈现阶段的人力,降低了一定的成本,但占主要部分的报表数据准备阶段仍未解决。

数据准备阶段的特点是:1. 编码困难,没有普适的工具 2. 对人员要求高,需要高水平技术人员完成 3. 实现周期过长,难以适应多变的报表需求 4. 硬编码耦合性高,依赖数据库和 JAVA 都都会造成紧耦合 5. 运维过于复杂,修改维护成本提高

这时,我们需要沿着第一步的方向继续前进,将数据准备阶段也工具化。比较好的方式是
在报表工具中增加用于数据准备的计算模块,将原来使用 SQL/ 存储过程 /JAVA 实现的数据准备算法,全部通过计算模块完成,使得报表开发彻底工具化,简化开发,降低成本。

计算模块的能力可以通过脚本来实现,在脚本中内置各种丰富的计算类库,让报表开发人员独立就能完成数据准备阶段的工作,从而全面接管报表的开发,而不再依赖其他专业程序员。

这里尤其要注意,报表计算模块需要具备这样一些能力。

1. 易于编码
包含丰富的计算类库,可以很方便地完成各类数据计算任务;必要时还能提供可视化的编辑调试环境进一步简化这个阶段的开发;

2. 支持热切换
计算模块需采用解释执行机制,这样可以很好地和前端报表呈现模板结合在一起,报表修改后可以实时生效,无需重启整个应用;

3. 支持多样性数据源
提供 RDB、NoSQL、文本、Excel、hadoop 等多样性数据源接口,报表直接基于多言行数据源开发,也可以进行混合计算(如 Excel 和 RDB 的表 join);

4. 高性能
计算模块的计算性能不能低于原来 SQL/JAVA 的计算性能,最好高于原来的实现方式。

报表开发全面工具化以后,就可以获得更高的报表开发效率,进一步降低报表开发成本。

3. 独立报表模块

报表开发全面工具化以后,就可以着手梳理应用结构,独立报表模块,从而将报表模块从业务系统中解耦出来。

1. 梳理数据源
首先梳理数据源,将报表业务相关的数据源都整理出来,以后的报表开发只需要和这些数据源打交道;同时为下一步剥离报表业务做准备。

2. 剥离报表业务
将数据源和业务应用中与报表相关的数据准备(复杂 SQL、存储过程、中间汇总表、自定义 JAVA 类)全部转移到报表工具中实现;同时将这些内容从数据库和业务应用中清除,完成报表业务剥离。

3. 独立报表模块
报表业务完全剥离后,在报表工具中实现的报表在物理上就可以独立于数据源和业务模块存储,不再与业务系统和数据源紧密耦合,完全独立报表模块

4. 调整应用结构
独立报表模块后,报表模块与业务模块共享数据存储;应用系统调用报表模块为用户输出报表结果;同时解释执行报表模块支持热切换,即改即用,无需重启应用。

独立报表模块以后,报表就可以单独修改和维护,清晰的应用结构会进一步降低报表开发成本。

4. 建设报表团队

报表模块独立后,建设专门的报表开发团队,不再需要高成本应用程序员或 DBA 参与报表开发,进一步降低报表开发成本。

报表开发人员就无需应对纷繁复杂的应用环境,更无需关系应用架构,只需根据已有架构开发报表就可以了。半年以上工作经验的技术人员就能胜任(其实,理工科应届毕业生就可以了,甚至高中学历都行)。

简化报表开发后,还可以尝试将持续的报表开发工作移交给客户方的本地运维人员,这样不仅能降低开发商的成本,还能提高对客户的响应速度。

5. 完善沟通机制

最后就是建立有效的沟通机制,减少交流中的误解。

一个简单可行的办法就是建立企业内部的报表知识库,技术形式上搞一个论坛即可。把以前做过的报表收集到论坛上加以评注,并提供搜索功能。

当有了新的报表需求时,可以搜索历史库中是否曾经做过类似的报表(出现过相同的业务术语)。历史报表中保留有相关代码或公式,而这些形式化的信息不会有歧义,这就能帮助新的开发人员正确理解业务术语。

甚至许多报表部分还可以被直接复用,在人员变动时也可以最大限度地保证业务知识的继承。


应对报表没完没了是一个长期的过程,是涉及技术、管理等诸多方面的综合性事务。当这些步骤完成以后就可以全方位应对没完没了的报表了。

扩展阅读:
应对报表没完没了的五个步骤
一劳永逸地“解决”没完没了的报表开发

结语

报表做不完,都赖数据源;报表开发快与慢,数据准备是关键。

通过提升报表工具的数据准备能力可以解决大部分报表开发过程中的深层问题, 从而通过改进生产工具来提升生产效率。这样,在选择报表工具时,除了能满足呈现方面的需求外,也要关注工具本身的数据准备能力,自然是越强越好。

关于当前主流报表工具在这方面的能力,可以查看 国内十大活跃报表 BI 产品深度对比及点评 ,其中有深入的评论。

我们还将持续关注报表开发中遇到的各类难题,对这方面感兴趣的同学可以进一步学习“乾学院”上的免费商业智能课程: http://www.raqsoft.com.cn/wx/course-Business-Intelligence.html