复杂多数据源报表join后再计算的简便方法

复杂数据源是报表开发的常见问题,比如不同数据库表先进行join运算,再进行后续的过滤分组排序等运算。JasperReport/Birt等报表工具有virtual data source或table join,可以一定程度地实现多数据源join后计算,但掌握起来并不容易。

集算器具有结构化强计算引擎,支持多样性数据源,集成简单,可以协助报表工具方便地实现此类需求,下面通过一个例子来说明多数据源join后计算的过程。

Sales是mysql数据库中的表,存储着多名销售员每天的多个订单,其中字段SellerId是销售员编号。emp是mssql数据库中的表,存储着销售员信息,其中字段EId是销售员编号,Name是销售员名字,Dept是部门名称。现在需要在报表中展现:订单编号、日期、金额、销售员名字、部门名称,条件是:订单日期在最近N天(比如30天)或者订单属于某几个受关注的部门(比如Markeding和Finance)。

由于订单编号、日期、金额来自于表sales,而销售员名字、部门名称来自于表emp,因此要进行不同数据库之间join运算,join之后还要进行条件过滤。部分源数据如下:

表sales

表emp

集算器代码:

A1=myDB1.query("select * fromsales")

这句代码从数据源myDB1查询出sales表的记录,myDB1指向mysql数据库。函数query用来执行SQL查询,可以接收外部参数。A1的计算结果如下:

A2=myDB2.query("select * fromemp")

这句代码从数据源myDB2查询出emp表的记录,myDB2指向mssql数据库。

A3=A1.switch(SellerId,A2:EId)

上述代码将A1中的SellerId字段替换成A2中对应的记录,关联字段为EId。A3的计算结果如下(蓝色字体表示该数据项包含下级成员):

当A2中找不到对应的记录时,函数switch默认保留A1中记录,对应的SellerId显示为空,效果类似于左连接。如果想进行内连接,应当使用选项@i,形如:[email protected](SellerId,A2:EId)

A4=A3.select(OrderDate>=after(date(now()),days*-1)||depts.array().pos(SellerId.Dept))

上述代码可对关联结果进行过滤,条件有2个,第1个条件是:订单日期在最近N天(对应参数days),表达式为OrderDate>=after(date(now()),days*-1)。第2个条件是:订单属于某几个受关注的部门(对应参数depts),表达式是depts.array().pos(SellerId.Dept)。运算符||表示逻辑关系“或”。

函数now可以取当前时间,函数date将当前时间转为日期,函数after可以算出相对时间,比如after("2015-01-30",-30)表示将时间后退30天,即2015-01-01。使用不同的选项,函数after还能以年、季、月、秒为单位计算相对时间。

函数array可以按分隔符将字符串变成集合,比如"Marketing,Finance".array()等于["Marketing","Finance"]。函数array的默认分隔符是逗号,也可以指定其他分隔符。函数pos可以找出成员在集合中的位置,比如["Marketing ","Finance"].pos("Finance")等于2,在逻辑关系中等于true。如果成员不在集合中,则返回null,在逻辑关系中等于false。

值得注意的是SellerId.Dept这种用法,这表示SellerId字段对应的记录的Dept字段。可以看到,用switch替换字段后,表之间的关联关系就可以用对象的方式来访问,这种方式直观简单,进行多表多层关联时会体现得更明显。

Days和depts都是来自于报表的参数,如果分别输入30、"Marketing,Finance",则A4的结果如下:

A5=A4.new(OrderID,OrderDate,Amount,SellerId.Name:Name,SellerId.Dept:Dept)

上述代码从A4中取得报表需要的字段,其中SellerId.Name和SellerId.Dept分别表示emp表中的员工名字和部门名称,运算符“:”表示重命名。A5的计算结果如下:

到此为止,报表需要的数据就全部计算出来了。最后只需用result A5将A5中的二维表返回报表工具。集算器对外提供JDBC接口,报表工具会将集算器识别为普通数据库,集成方案请参考相关文档。

接下来以JasperReport为例设计一张简单报表,表样如下:

需要定义两个报表参数pdays、pdeps,分别对应集算器中的两个参数。预览后可以看到报表结果:

报表调用集算器的方法和调用存储过程一样,比如将本脚本保存为afterjoin1.dfx,则在JasperReport的SQL设计器中可以用afterJoin1 $P{pdays},$P{pdepts}来调用。

有了esProc的协助,报表工具还可以处理更复杂的多数据源join后计算。比如:找到某日期之后每个销售员的销售额增长最快的三天,展现销售员名字、这三天的日期、销售额、增长率。

集算器代码:

A1=myDB1.query("select * from sales whereOrderDate>=?",beginDate)

上述代码用来查询表sales中某日期之后的订单,其中beginDate为报表传来的参数,假设该值为”2015-01-01”,则A1的计算结果如下:

A2=myDB2.query("select * from emp")

上述代码查询emp表,结果如下:

A3=A1.switch(SellerId,A2:EId)

上述代码将A1中的SellerId字段替换成A2中对应的记录,结果如下:

A4=A3.group(SellerId)

上述代码将订单按SellerId分组。下图左侧是A4的计算结果,右侧是其中两个SellerId的订单。

A5=A4.(~.groups(OrderDate,SellerId;sum(Amount):subtotal))

上述代码将每个SellerId的订单再按照OrderDate和SellerId分组,并汇总各组的订单金额,即:每个销售员每天的销售额。如下图:

上述代码中“A4.()”表示对A4的每个成员进行循环计算,括号中的“~”是成员变量,即某SellerId对应的订单记录。“~.groups()”表示对每个成员应用函数groups。函数groups可对数据分组并进行简单的汇总,函数group可以只分组不汇总。

A6=A5.(~.derive((subtotal-subtotal[-1])/subtotal[-1]:rate))

上述代码计算出每个销售员销售额的日增长率,计算结果如下:

上述代码中,函数derive用来增加新字段,字段名为rate,算法为“(当日销售额-上一日的销售额)/上一日的销售额”。可以看到,集算器用subtotal[-1]来表示上一日的销售额,可以轻松进行相对位置的计算。

值得注意的是,由于第一条记录没有对应的“上一日的销售额”,因此销售额增长率为Null。

A7=A6.(~.select(#!=1))

这句代码在A6的基础上去掉每组数据的第一条记录(因为第一条记录的增长率为无意义的Null)。

代码中的函数select可以进行查询,“#”表示循环序号,“#!=1”即序号不等于1。同样的功能也可以用函数delete来实现,但性能稍低,这是因为函数select只返回引用,而delete需要改变实际数据。

A8=A7.(~.top(-rate;3))

上述代码可以计算出每个销售员销售额增长率最大的三天对应的记录。函数Top可以根据某字段(或某几个字段的表达式)取出前N条记录。计算结果如下:

A9=A8.union()

上述代码将A8中的各组数据合并起来,形成新的二维表A9,如下:

A10=A9.new(SellerId.Name:Name,OrderDate,subtotal,rate)

上述代码从A9中取出需要的字段,即本案例最终计算结果。

result A10

上述代码将A10中的二维表返回报表工具。报表的设计可以参考前一个案例,这里不再赘述。

时间: 2024-11-09 02:37:22

复杂多数据源报表join后再计算的简便方法的相关文章

润乾报表实现动态数据源报表及改进

经常会遇到一些报表需要根据不同的情况(参数)连接不同的数据源从而完成相应的数据的展现,也就是经常说的动态数据源报表.报表工具通常的做法有两种,一是不同的情况加载不同的数据源连接参数,如:url.driver.username.password等:二是利用已配置的多个连接池,根据不同情况选择.这里通过一个实例,说明润乾报表的实现过程及改进方案. 报表说明 应用中需要通过参数控制报表连接的数据源,当参数flag为1时连接数据源一(db1),否则连接数据源二(db2). 润乾报表实现 首先根据flag

多数据源报表解析之简单多源报表

多数据源报表即一张报表中可以定义多个数据集,分别取出需要的数据库表,所取的数据库表甚至可以来自于不同的数据库.本文通过几个例子说明多个数据集数据如何相互关联来实现多源报表. 1. 描述 多数据源,就是在同一张报表当中,显示的数据来自于多个不同的表或不同的库. 如下图一张简单的多数据源报表,左侧蓝色部分来自于销量表,右侧***部分来自销售总额表,即数据来自于两张不同的数据库表: 2. 示例 2.1 打开单个数据源报表 打开报表%FR_HOME%\WebReport\WEB-INF\reportle

ubuntu 14.04 安装 vmware 10 X64 后无法启动解决方法

ubuntu 14.04 安装成功后,平时的工作什么的都够用了, 最近需要做一个测试,测试环境还必须是windows的.所以就准备整个虚拟机来,kvm 跟 vmware 考虑再三,选择了 vmware .vmware 10的安装方法,就不做说明了.度娘或谷哥还是有蛮多方法.我这里所写的是,vmware 安装后,启动时报错. 报错图片如下: 解决方法: 当然方法有很多种,我也试过几种方法,有使用patch 的,但是我用这种方法的时候,出现错误了,继续找方法的时候,有网友有告诉过这样一种方法,就是手

angular指令监听ng-repeat渲染完成后执行自定义事件方法

今天工作中遇到需要用到ng-repeat遍历渲染完后执行某个操作,angular本身并没有提供监听ng-repeat渲染完成的指令,所以需要自己创建自定义指令. 在ng-repeat模板实例内部会暴露出一些特殊属性$index/$first/$middle/$last/$odd/$even,$index会随着每次遍历(从0开始)递增,当遍历到最后一个时,$last的值为true,所以可以通过判断$last的值来监听ng-repeat的执行状态, 怎么在遍历过程中拿到$last的值:自定义指令 v

WPF中实现先登录后启动主程序的方法

[转载] http://blog.csdn.net/swarb/article/details/17301167 WPF中实现先登录后启动主程序的方法 我觉得先登录后启动应用主程序是一个很经典的问题,基本上如果要写一个应用程序都会用到这个的小环节.我在这个问题上挣扎了大半天才找到解决方案,我的实现方法我觉得有点不正宗,如果有哪位高手知道更好的方法欢迎留言指导!! 首先来说一下传统C#在WinForm中的实现方法,基本上是在Main函数中根据第一个启动窗口的DialogResult来判断是否实例第

Winform开窗,筛选数据后返回数据的方法

在开发中,经常需要打开另一个窗体(简写为"开窗"),然后在开窗中进行数据筛选,选中需要的数据,最后将值传递给本原来的窗体.而且,这个开窗可以重复用于多个地方,其效果如同日历控件的弹出窗口.如下图所示: 测试环境 vs2008 基本思路 1.创建一个窗体类. (1)为该类添加用于传递值的属性. (2)为该类添加一个事件,用于通知调用方值已经准备好. (3)在窗体类的某个函数中,如单元格双击处理函数中,为属性赋值,并引发这个事件. 2.调用该窗体类. (1)定义一个全局的窗体类对象. (2

二叉树前序、中序和后序的遍历方法(递归、用栈和使用线索化)

在2^k*2^k个方格组成的棋盘中,有一个方格被占用,用下图的4种L型骨牌覆盖所有棋盘上的其余所有方格,不能重叠. 代码如下: def chess(tr,tc,pr,pc,size): global mark global table mark+=1 count=mark if size==1: return half=size//2 if pr<tr+half and pc<tc+half: chess(tr,tc,pr,pc,half) else: table[tr+half-1][tc+

登陆后设置cookie的方法

public void SetCookie(string userName, string role,string cookieValueName) {FormsAuthentication.FormsCookieName FormsAuthenticationTicket myTick = new FormsAuthenticationTicket(1, userName, System.DateTime.Now.AddMinutes(30),DateTime.Now, false, role

一对一关联查询时使用relation连贯操作查询后,调用getLastSql()方法输出的sql语句

如题: 一对一关联查询时使用relation连贯操作查询后,调用getLastSql()方法输出的sql语句不是一条关联查询语句. 例如: $list = $db->relation(true)->where($where)->order('blogid desc')->limit($Page->firstRow.','.$Page->listRows)->select(); $sql = $db->getLastSql(); 输出的sql语句为: SELE