当前位置: 首页 > 知识库问答 >
问题:

加速apachepoi-SUMIF

傅兴平
2023-03-14

在xlsx工作簿中,有些单元格具有一些无界的SUMIF公式,如下所示:SUMIF(MySheetname!$B:$B,$E4,MySheetname!$I:$I)。使用ApachePOI5.0.0对一个SUMIF函数的评估持续100ms,对给定工作簿的评估持续几分钟。

提高执行持续时间的一种方法是将公式绑定到如下内容:SUMIF(MySheetname!$B1:$B100,$E4,MySheetname!$I1:$I100)。在我的情况下,这不是一个解决方案,因为我不是xlsx文件的作者,系统从未知的人那里获取未知的xlsx文件(因此我不能仅仅告诉他们限制SUMIF范围)。

org.apache.poi.ss.formula.functions.Sumif的当前实现迭代给定(无界)范围内的所有单元格,因此每个计算迭代1048576个单元格。

这是方法sumMatchingCells(AreaEval、I_MatchPredicate、AreaEval)实现的一部分。

for (int r=0; r<height; r++) {
    for (int c=0; c<width; c++) {
        result += accumulate(aeRange, mp, aeSum, r, c);
    }
}

我想通过检查行或列是否实际存在于求和范围来提高这种方法的性能。也许是这样的(使用不存在的方法sheetContainsRowIndex):

for (int r = 0; r < height; r++) {
    if (aeSum.sheetContainsRowIndex(aeSum.getFirstRow() + r)) {
        for (int c = 0; c < width; c++) {
            if (aeSum.sheetContainsColumnIndex(aeSum.getFirstColumn() + c)) {
               [...]

LazyAreaEval包含一个SheetRangeEvaluator,其中包含SheetRefEvaluators,其中包含一个EvaluationSheet,这至少知道getLastRowNum()。不幸的是,这个属性链是私有的。

你知道如何做到这一点吗?或者其他如何提高SUMIF执行性能的想法?

共有1个答案

殳毅
2023-03-14

修补apache poi公式计算需要深入研究源代码并搜索计算过程。我什么也不会做。

但一种解决方法是,在计算之前,将公式中的所有整列引用替换为表中从第1行到最后一行的区域引用。

如果只读取工作簿,则这只会影响随机访问内存,而不会影响存储的文件。当然,如果需要保存已更改的工作簿,则会影响存储的文件。那么解决方案可能不可用。

当工作表中有多个公式具有完整的列引用时,这对流程持续时间有明显的影响,至少使用*. xlsxXSSF),尽管需要对每个公式进行额外的替换过程。

完整的代码示例:

import java.io.FileInputStream;

import org.apache.poi.ss.formula.*;
import org.apache.poi.ss.formula.ptg.*;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.*;
import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.ss.SpreadsheetVersion;

public class ExcelEvaluateFullColumnFormulas {

 private static String replaceFullColumnReferences(XSSFSheet sheet, String formula) {
  //System.out.println(formula);
     
  XSSFWorkbook workbook = sheet.getWorkbook();
  XSSFEvaluationWorkbook evaluationWorkbook = XSSFEvaluationWorkbook.create(workbook);
  
  Ptg[] ptgs = FormulaParser.parse(formula, (FormulaParsingWorkbook)evaluationWorkbook, 
   FormulaType.CELL, sheet.getWorkbook().getSheetIndex(sheet));
   
  for (int i = 0; i < ptgs.length; i++) {
   if (ptgs[i] instanceof AreaPtgBase) { // the operand Ptg is an area reference
    AreaPtgBase ref = (AreaPtgBase) ptgs[i];
    if (ref.getFirstRow() == 0 && ref.getLastRow() == SpreadsheetVersion.EXCEL2007.getLastRowIndex()) { // only for full column area references
     int lastRowInSheet = SpreadsheetVersion.EXCEL2007.getLastRowIndex();
     if (ref instanceof Area2DPtgBase) { // the area reference is a 2D area reference in same sheet
      lastRowInSheet = sheet.getLastRowNum(); // get last row of this sheet
     } else if (ref instanceof Area3DPxg) { // the area reference is a 3D area reference in another sheet
      Area3DPxg ref3D = (Area3DPxg)ref; 
      String sheetName = ref3D.getSheetName();
      lastRowInSheet = workbook.getSheet(sheetName).getLastRowNum(); // get last row of referenced sheet
     }      
     ref.setLastRow(lastRowInSheet);
     formula = FormulaRenderer.toFormulaString((FormulaRenderingWorkbook)evaluationWorkbook, ptgs);
    }
   }
  }
  //System.out.println(formula);
  return formula;
  
 }

 public static void main(String[] args) throws Exception {

  DataFormatter formatter = new DataFormatter();
  Workbook workbook = WorkbookFactory.create(new FileInputStream("test.xlsx"));  
  FormulaEvaluator evaluator = workbook.getCreationHelper().createFormulaEvaluator();

  Sheet sheet = workbook.getSheetAt(0);
  java.time.LocalDateTime startTime = java.time.LocalDateTime.now();
  for (Row row : sheet) {
   for (Cell cell : row) {
    ///*
    if (cell.getCellType() == CellType.FORMULA) {
     if (sheet instanceof XSSFSheet){ // do it for XSSF only, not necessary for HSSF.
      String formula = cell.getCellFormula();
      formula = replaceFullColumnReferences((XSSFSheet)sheet, formula);
      cell.setCellFormula(formula);
     }
    }
    //*/
    String value = formatter.formatCellValue(cell, evaluator);
    System.out.print(value + "\t");
   }
   System.out.println();
  }
  
 java.time.LocalDateTime endTime = java.time.LocalDateTime.now();
 java.time.Duration duration = java.time.Duration.between(startTime, endTime);
 System.out.println("process duration: " + duration);
 
 workbook.close();
 }
}

评论这部分

...
    /*
    if (cell.getCellType() == CellType.FORMULA) {
     if (sheet instanceof XSSFSheet){ // do it for XSSF only, not necessary for HSSF.
      String formula = cell.getCellFormula();
      formula = replaceFullColumnReferences((XSSFSheet)sheet, formula);
      cell.setCellFormula(formula);
     }
    }
    */
...

看看有什么不同。

 类似资料:
  • 我有点迷失了 我试过3.17、4.0.0和5.0.0版。 或 我无法获得没有弃用或类型错误的代码:-( 我将Eclipe与Maven和Java11一起使用。在版本发布之后,我做了“更新项目”来更新Maven。

  • 在执行此代码时,它在线程"main"java.lang.NoClassDefFoundError: org/apache/Commons/math3/util/ArithmeticUtils在org.apache.poi.poifs.property.RootProperty.set大小(RootProperty.java:59)在org.apache.poi.poifs.property.Dir

  • 你可以通过执行npm run preview -- --report来分析webpack打包之后的结果,观察各个静态资源的大小。你可以发现占用空间最多的是第三方依赖。如vue、element-ui、 ECharts等。 你可以使用 CDN 外链的方式引入这些第三方库,这样能大大增加构建的速度(通过 CDN 引入的资源不会经 webpack 打包)。如果你的项目没有自己的CDN服务的话,使用一些第三

  • 加速计 jd.startAccelerometer(Object object) 开始监听加速度数据。 参数 Object object 属性 类型 默认值 必填 说明 interval string normal 否 监听加速度数据回调函数的执行频率 success function 否 接口调用成功的回调函数 fail function 否 接口调用失败的回调函数 complete funct

  • 我正在尝试用ApachePOI创建甜甜圈图,但没有任何信息或示例。我尝试使用饼图的例子,但没有成功。你能帮我解决这个问题吗?

  • Julia有一个非常好的特性,可以访问自己的语法树,这使得以编程方式生成新函数变得很容易,但它比普通的Julia代码慢得多。 例如: 有没有办法给提供与普通Julia代码相同的速度? @Ivarne建议我提供我的项目的细节。我想使用Julia的元编程功能来计算符号导数并运行它们。 我编写了一个函数,它接受and表达式和一个参数,并返回一个新表达式,该表达式是相对于的导数。不幸的是,结果需要太长时间