统计报表中巧用宏

在面对海量基础数据时,统计报表的性能往往是重中之重.速度仿佛成了取胜之匙.我们在分析时发现,往往对于这种大数据量统计报表来说,直接面向基础数据进行处理有的时候并不是明智之举,因为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}”)

这样的话,整个过程通过报表宏得到了很好的解决.

总结:利用报表中强大的宏功能,我们可以完成多种报表需求,例如报表复用(利用宏动态改变表达式),报表排序等等.

热门文章