统计报表中巧用宏
在面对海量基础数据时,统计报表的性能往往是重中之重.速度仿佛成了取胜之匙.我们在分析时发现,往往对于这种大数据量统计报表来说,直接面向基础数据进行处理有的时候并不是明智之举,因为sql执行速度也有瓶颈.这时,我们往往要用到中间表来提高速度.那么,在有并发的时候,如何来保证所抓取的数据都是最新的呢?
我们这里提供一种思路:
写一个存储过程,先清掉中间表的数据,然后进行数据抓取,填充到中间表中.利用报表的宏来执行这个存储过程,让报表抓取的数据都是最新的.
报表宏在整个过程中很关键,由于它的特性是在报表运算之前,系统会全面搜索整张报表的表达式定义,将所有的宏名替换成宏值.所以把执行存储过程的重任交给报表宏是再合适不过了.
首先,我们需要建立一个存储过程,来完成从基础表中抓取数据到中间表中的功能.由于这并不是本文的重点,此处省略存储过程的写法.
然后,我们分析以下存储过程的内容,由于它只是完成抓取并填充数据的过程,并无返回值,所以,我们还需要写一个自定义函数来执行.
例如:
import java.sql.CallableStatement;
import java.sql.Connection;
import java.util.Map;
import com.runqian.base4.resources.DataSetMessage;
import com.runqian.base4.resources.MessageManager;
import com.runqian.base4.util.Queue;
import com.runqian.base4.util.ReportError;
import com.runqian.base4.util.SQLTool;
import com.runqian.base4.util.Stack;
import com.runqian.report4.dataset.DataSet;
import com.runqian.report4.model.expression.Expression;
import com.runqian.report4.model.expression.Function;
import com.runqian.report4.model.expression.Node;
import com.runqian.report4.model.expression.Variant2;
import com.runqian.report4.usermodel.Context;
import com.runqian.report4.usermodel.DataSourceConfig;
import com.runqian.report4.usermodel.IConnectionFactory;
import com.runqian.report4.usermodel.IReport;
public class ExecProc extends Function {
@Override
public Object calculate(final Context cxt, final boolean arg1) {
// TODO Auto-generated method stub
try
{
final String dbName = cxt.getDefDataSourceName();
final DataSourceConfig dsc = cxt.getDataSourceConfig(dbName); //获取系统数据源配置
IConnectionFactory conFactory = null; //数据连接工厂
Connection cn = cxt.getConnection(dbName); //获取数据库连接对象
if (cn == null ) {
conFactory = cxt.getConnectionFactory(dbName); //未取到数据库连接,取同名数据连接工厂
if (conFactory == null){ //无法获取正确数据库连接,返回错误信息
final MessageManager mm = DataSetMessage.get();
throw new ReportError(mm.getMessage(“error.noConnection”, dbName, dbName));
}
cn = conFactory.getConnection();
}
if ((cn == null) || (cn.isClosed()))
throw new ReportError(“过程调用函数执行异常,无有效数据库连接”);
String dbCharset = dsc.getDBCharset(); //获取数据库字符集
if (dbCharset == null) dbCharset = “GBK”;
String clientCharset = dsc.getClientCharset(); //获取本地字符集
if (clientCharset == null) clientCharset = “GBK”;
// String encoding = this.env.getDBCharset();
if (this.paramList.size() == 0)
throw new ReportError(“过程调用函数参数列表为空”);
final Expression param1 = (Expression)this.paramList.get(0);
final Object result1 = Variant2.getValue(param1.calculate(cxt, false),false,false);
//Object result1 = Variant2.getValue(arg0, arg1, arg2)(param1.calculate());
String sql = null;
if (result1 instanceof String) {
sql = (String)result1;
}
else
throw new ReportError(“过程调用函数参数1应为字符串”);
final CallableStatement proc = cn.prepareCall(sql);
final int rn = 0;
int i = 1; for (final int isize = this.paramList.size(); i < isize; ++i) {
final Expression param = (Expression)this.paramList.get(i);
if (param == null)
throw new ReportError(“过程调用函数出现无效参数”);
final Object result = Variant2.getValue(param1.calculate(cxt, false),false,false);
SQLTool.setObject(1, proc, i, result, 9);
}
proc.execute();
proc.close();
return null;
}
catch (final Exception e)
{
throw new ReportError(“过程调用函数sql异常:” + e.getMessage(), e);
}
}
}
此自定义函数的功能就是执行这个存储过程,然后返回一个null.
接着,我们在报表中部署以下这个自定义函数,在customFunctions.properties中写入
ExecProc =0,ExecProc
这样自定义函数就部署好了.
最后,我们在报表里增加一个宏,然后调用:
例如:ExecProc(“call {procTest}”)
这样的话,整个过程通过报表宏得到了很好的解决.
总结:利用报表中强大的宏功能,我们可以完成多种报表需求,例如报表复用(利用宏动态改变表达式),报表排序等等.