如何用报表工具实现树状层级结构的填报表

【摘要】

数据填报中,表头项如果是科目或者地区等有层级关系的维度数据,常常会希望表头能以树状形式展示,以便用户能更直观的理解填报业务,方便录入数据,点击 如何用报表工具实现树状层级结构的填报表查看如何操作

需求说明

对于带有层级结构的数据中,用户为了能够更加清晰直观地查看,往往需要在数据展示时将层级展示出来,比如常见的省、市、县结构,或者一些科目中也会带有层级。通常,我们管这种形式叫做树状报表。在查询统计类报表中可以使用报表的左主格来实现,但是由于填报模型更加侧重于数据处理,格式设计上有别于查询统计报表,往往较难实现树状报表。

下面,我们通过一个层级科目的例子介绍一下在填报表中如何实现这个需求。首先,我们看一下报表展示的结果:

1jpg

这个报表是一个按照科目录入数据的填报表,科目分不同等级,比如 1001 为第一层,下属 100101、100102,然后下边又有不同科目。展示时要求能够按层级展示。

解决方案

此类报表根据数据库中数据层级要求不同,有不同的解决方案:

一:科目固定不变

比如省、市、县,行政区域极少发生变动,针对这种固定不变的,可以制作固定的报表格式,如下图:

2jpg

报表设计时的格式和展现格式完全一致,因此技术门槛低,但是工作量大,如果是几十个科目,就需要设计几十行,如果更多,需要耗费的时间也就更多了。并且如果万一科目发生变化,后续的维护工作也会十分不便。

二:数据库中数据已经按层级存储好

在数据库中,科目顺序以及层级等都已经按照要求存储好了,此时填报表只需要按照正常的清单格式制作即可,如下图:

3jpg

这种方式报表开发量小,但对数据库数据有特定要求。如果数据库中新增了科目,那么数据库中的数据顺序及格式都需要调整,以满足严格要求,才能达到效果,例如不同层级的前方有相应的缩进。这时往往需要人工对数据进行维护,因此实际应用中有一定的限制性。

三:数据库中数据灵活存储,有相应层级关联关系

上述两种方式对数据自身的要求都比较严格,而且都不利于系统维护。下面这个例子应该才是大家真正关心的,也就是在对数据库数据无特殊要求的情况下,如何实现一个带层级结构的树状填报表。

当然,如果要实现层级,数据格式不能是完全杂乱无章的,比如以下数据:

4jpg

CODE 字段存储的是科目,科目中的层级关系是:1001 这种四位的为第一层级,然后六位为第二层级,并且第二层级通过 CODE 的前四位来确定属于哪个第一层科目,第三层同样规律。这也就是说,要想实现层级结构,数据中是必须要有相应规律的。

需求实现

数据结构和层次规则确定了,接下来看看如何在填报表中实现该需求。这个需求的难点主要是如何设置层级,由于层级是动态的,单写 sql 很难实现,所以就需要借助数据处理利器:集算器。在集算器中,可以根据规律生成想要的数据格式,具体脚本如下:

5jpg

A1:连接数据源

B1:执行 sql,从数据库中取出需要的数据

C1:按照 B1 的结果,生成一个空序表,用于存放生成的数据,该序表中有四个字段:CODE、NAME、QCYE、QMYE

A2:=B1.select(len(CODE)==4),从 B1 中取出 CODE 长度为 4 位的数据,即第一层数据。

A3:for A2,按照 A2 做循环,本例的思路就是按不同层级依次循环,然后取出下级数据拼接进去。

B4:>C1=C1|A3,C1 是之前定义的空序表,这里将 A3 的内容插入到 C1 中,A3 为每次循环的单条记录,此例中是第一层级数据

C4:=B1.select(len(CODE)==6 && left(CODE,4)==A3.CODE).run(if(CODE!=null," "+CODE):CODE),该表达式可以拆分成两步操作,select 是按照条件从 B1 中取出 CODE 长度为 6 位,并且前四位和第一层级相同的数据,即第二层。由于报表展示时,第二层前方需要缩进几个空格,所以此处在通过 run 函数,在科目前增加几个空格,并且可以增加判断,当 CODE 不为空时,也就是有子层级时再操作。这样就在 C4 里按照 A2 做循环取出了第二层。

C5:for C4,按照第二层再次做循环,要取第三层数据

D6:>C1=C1|C5,在 C1 的基础上再将 C5(第二层)插入进去,这样 C5 中就有两层数据了。

D7:=B1.select(len(CODE)==8 && left(CODE,6)==trim(C5.CODE)).run(if(CODE!=null," "+CODE):CODE)

该表达式同 C4,从 B1 中取出 CODE 长度为 8 位并且左六位为第二层 CODE 的值,也就是取出第三层数据,注意,由于第二层要设置缩进,在 C4 单元格中在 CODE 前插入了空格,所以此处条件关联时用 trim 函数去掉空格,再通过 run 函数在 CODE 前增加相应空格,具体缩进多少,增加几个空格即可。

D8:>C1=C1|D7,再将 D7 取出来的数据追加到 C1 中。

这样通过两个循环,就能够实现层级格式,此时 C1 中的结果如下:

6jpg

NAME 字段在原数据库中已经有相应空格了,所以集算器中并没有对其做处理,如果数据库中数据没有空格,也想做缩进的话,通过 run 函数在循环中一并处理即可。

A9:>taizhang=C1.new(CODE:CODE,NAME:NAME,QCYE:QCYE,QMYE:QMYE),C1 中数据是个序列,此处通过 new 取出相应数据生成序表设置给对象 taizhang,在填报表中使用。

A10:通过 close() 函数关闭数据库连接。

将该集算器文件保存为 get.dfx 供填报表使用。

数据来源

在填报表中,设置:数据处理——数据来源,采用 DFX 文件,选择刚才保存的 get.dfx 文件:

7jpg

填报表制作

数据都已在取数 dfx 文件中处理完毕,所以报表格式设计起来就方便很多,如下图:

8jpg

标题、表头按照需求设置。

A3 单元格类型设置成维度格,表达式设置成:=taizhang.(CODE),字段名称设置成:taizhang.CODE

B3、C3、D3 单元格类型设置成数值格,字段名称为别设置为:NAME、QCYE、QMYE

这样报表就能够取出相应的数据进行展示,展示结果如下图:

9jpg

数据去向

要想将数据保存回数据库,需要在数据去向中进行回写设置,此处同样可以使用 dfx 文件,脚本如下:

10jpg

A1:连接数据源

A2:>[A1.update@k(taizhang:taizhang_old,TAIZHANG,CODE:trim(CODE),NAME,QCYE,QMYE;CODE)](mailto:A1.update@k(taizhang:taizhang_old,TAIZHANG,CODE:trim(CODE),NAME,QCYE,QMYE;CODE)),通过 update 函数向数据库中 taizhang 表回写数据,具体可参看 update 函数说明,注意:之前取出时在 CODE 前加了空格做数据缩进,所以回写时要通过 trim 函数去掉空格。

A3:=A1.error@m(),看保存过程中是否有错误

A4:>if(A3==null,A1.commit(),A1.rollback()),如果错误(A3)为空,则提交,否则回滚事务

A5:关闭数据链接。

另外,还需要在保存对象的旧对象中设置:

11jpg

这样,就能够完成填报表的树状层级结构。

后记:

通过此例可以看到,如果在集算器中对数据进行处理,事先形成层级树状结构数据,那么在报表中只需要直接使用集算器处理后的数据就行了。本例中通过科目的 CODE 位数确定层级,而在其它不同的数据结构中可能会有不同的形式,但必然会有相应的规律可循,只需要在集算器中按照相应规律整合数据。

小遗憾:这种方式灵活方便,但是报表数据扩展前在设计器中都在同一个单元格内,如果需要页面端按照层级自动计算,比如第三级数据更改后相应的第二级数据需要自动变化,这样就很难写对应的自动计算公式,but,报表展现后是可以通过 javascript 对页面中数据做处理的,所以可以在 js 中根据层级规律设置自动计算公式来实现。
更多填报类问题请看:填报分类导航
* 固定格式填报表的制作
* 分组填报表的制作
* 脚本中如何做填报数据校验
* 填报表之数据留痕
* 行式填报之主键设置