百万级分组大报表开发与呈现
在《秒级展现的百万级大清单报表怎么做》(http://c.raqsoft.com.cn/article/1535506545563?r=shiguang),中,我们介绍了无论RDB还是非RDB,润乾报表都能够通过异步线程实现秒级海量大清单报表(以下简称:大报表)。实际业务中,除了查询明细,有时还要展现全量汇总数据,或者查询分组明细和计算分组汇总。本文就将介绍这些带有汇总和分组的大报表的开发方法。
带汇总的大报表
在大报表中计算汇总值与常规报表基于报表内数据进行的汇总不同,大报表由于采用异步线程,因此无法通过报表内数据进行汇总(因为每次只能取到部分数据),只能在数据处理阶段计算汇总值并交给报表呈现。
我们还是使用《秒级展现的百万级大清单报表怎么做》中SQL源大报表的例子,现在需要在每页最后一行显示订单汇总情况(订单数量、订单总额、运费总额)。
制作报表模板
增加一个数据集ds2计算汇总值:
报表中最后一行引入ds2汇总值进行呈现,这里为了每页都包含汇总数据,设置最后一行属性为报表尾。
当然,带有汇总的大报表性能跟计算汇总值的SQL执行效率强相关,所以可能会发现带有汇总的大报表要比单纯查询明细慢一些。
分组大报表
实际业务中,简单呈现大报表清单往往还不够,有时还需要对海量数据进行分组,呈现汇总及明细情况。下面,我们就以订单数据为例,查询一下按地区分组的明细及订单金额汇总。
需求分析
首先,需要呈现分组明细的报表无法在数据源端进行聚合,例如通过SQL聚合后就不包含分组明细了,除非再查询一次进行拼接,但查询两次显然会严重影响性能。因此我们需要尽量将明细数据读出后在应用端进行分组聚合。
那问题就来了,由于海量数据要通过分批读取的方法进行呈现,如何保证读取的每批分组数据是完整的?如果分组数据不完整,分组聚合的结果显然也就不正确了。因此,除了要保证能在应用端实现聚合,还要保证批量读取数据时分组数据的完整性。
下面就是润乾报表结合集算器(数据集)实现分组大报表的过程。
报表数据准备
我们需要编写集算器SPL脚本进行数据准备。这里,我们要编写两个SPL脚本,分别实现:查询分组明细并计算汇总,以及通过游标分批为报表返回结果集。
SPL脚本1:group-detail.dfx
设置脚本参数:
编写SPL脚本:
|
A |
B |
C |
1 |
=connect("db") |
|
|
2 |
=A1.cursor@x(“select 货主地区,订单ID,客户ID,订购日期,运货费,订单金额 from 订单 where 订购日期>=? and 订购日期<=? order by 货主地区”,begin,end) |
/建立数据库游标查询订单数据,并按地区排序 |
|
3 |
for A2;货主地区 |
/按地区分组读取数据 |
|
4 |
|
=A3.derive(0:flag) |
/增加标志位 |
5 |
|
=B4.sum(订单金额) |
/分组汇总 |
6 |
|
>B4.insert(0,null,null,null,null,"订单金额小计:",B5,1) |
/汇总值添加到明细数据中 |
7 |
|
return B4 |
/返回明细及分组结果 |
SPL解析:
- A2查询数据库,SQL中指定按照分组字段(货主地区)排序,以便后续可以每次取出整个分组进行汇总。为此需要在排序字段上建立索引,避免全表排序时间过长。
- A3循环游标,以货主地区为标记,保证每次读取的记录数为一个地区数据
- B4增加标志位,用于后续报表展现中突出汇总行。标志为0时表示是明细记录
- B5针对每个分组进行汇总
- B6将分组值追加到明细记录中,标志设为1,表示是汇总记录
- B7返回分组明细和汇总集合。注意这里return写在循环内,因此会多次返回分组集合
关于for cs,n;x用法
在集算器SPL脚本中,for cs,n;x 表示针对游标cs通过循环遍历数据,每轮从游标读取n条记录或者直到记录中的x发生变化,循环全部结束后关闭游标。大数据量的分组取数是这种循环的常用之处。如果省略了n和x,那就简单地返回游标中所有数据并关闭游标。函数的具体说明可以参考:http://doc.raqsoft.com.cn/esproc/func/forcsnx.html
当大数据集按照分组字段有序时,这种取数方式每次可以读取一个完整分组到内存中参与计算,不过这时仍然要求分组不能很大(内存能装下)。而在金融和电信行业中,经常要基于单用户做数据分析,也就是按用户分组,而每个用户的流水记录规模又较大,常规的拆分方法作起来十分复杂。而通过集算器就可以很好地解决这类问题了。
SPL脚本2:main.dfx
设置脚本参数:
编写SPL脚本:
|
A |
B |
1 |
=cursor("group-detail.dfx",begin,end) |
/调用dfx生成游标 |
2 |
return A1 |
/为报表返回游标 |
前一个SPL脚本group-detail.dfx解决了分组查询明细并汇总计算的问题。这个脚本main.dfx则可以分批次取数并提供给润乾报表,从而实现异步大报表呈现。脚本中A1通过cursor函数直接调用前一脚本生成游标,由A2将游标返回给报表。
关于cursor()函数
使用cursor函数调用SPL脚本生成游标时,被调用的SPL脚本可以有多个返回结果集(例如在for循环中的多个return),而游标取数(fetch)时可以依次使用多个return结果,无需等待所有结果集都准备好再使用,原理如下图所示。函数的具体说明可以参考:http://doc.raqsoft.com.cn/esproc/func/cursordfx.html
结合大报表使用cursor()函数运行原理
设计报表模板
设置报表参数,查询起止日期:
设置数据集引用main.dfx并传递日期参数:
按照大报表模板设计思路,编写表达式
为了将汇总行高亮显示出来,这里利用了数据准备阶段增加的标志位flag列,当flag值为1的时候代表该行为汇总行,设置背景色表达式:if(A3==1,-3355444)
设置大数据集
发布到WEB
将做好的模板发布到WEB端,效果如下:
当然,标志位flag列也可以设置为隐藏。
效果调整
调整报表模板,将标志位flag列隐藏,并设置B3格的扩展属性 同值合并为“纵向合并”
展现时分组列则带有合并格的效果:
注意事项
在《秒级展现的百万级大清单报表怎么做》中,我们提示了大报表不要全表排序,也不适合高并发场景。除此以外,对于带有分组汇总和明细的大报表还应该注意:
单个分组不宜过大
由于计算分组明细和汇总值时需要将某一个分组数据全部加载到内存中进行计算,因此分组相对内存容量不宜过大,从而确保单个分组数据能进行全内存计算。
原文来自于润乾乾学院,地址为:http://c.raqsoft.com.cn/article/1536328795758?r=shiguang