Apache POI和SUBTOTAL公式

问题描述 投票:0回答:1
=SUMPRODUCT((K:K="yes")*(SUBTOTAL(103,OFFSET(K10,ROW(K:K)-MIN(ROW(K10:K10)),0))))

=SUMPRODUCT((K:K="yes")*(SUBTOTAL(103,OFFSET(K10,ROW(K:K)-MIN(ROW(K10:K10)),0))))+(NOW()*0)

这是使用Java poi api在生成的xls工作簿中的工作表之一中使用的excel公式。仅当我在Excel中的单元格上按Enter时,它才能正确评估。公式计算器和wb.setForceFormulaRecalculation(true)似乎无效。

Java代码是:

cell.setCellFormula("SUMPRODUCT((K:K=\"yes\")*(SUBTOTAL(103,OFFSET(K10,ROW(K:K)-MIN(ROW(K10:K10)),0))))");

公式的目标是计算K列中“是”的出现,但仅针对过滤后的可见行。 K10是实际数据开始的单元格。此单元格上方的行包含标题。

excel excel-formula apache-poi worksheet-function
1个回答
0
投票

我可以使用HSSF确认这是一个问题。但这与公式重新计算无关。 OFFSET本身和NOW一样易失。即使不设置setForceFormulaRecalculation(true),两个强制公式也会重新计算。但是在二进制*.xls文件中,ROW(K:K)中的OFFSET最初不评估为数组。因此,仅对1ROW(K1))而不是对数组{1,2,3,4,5,...}ROW(K1), ROW(K2), ROW(K3), ...)求值。使用XSSF*.xlsx)即可。

我发现问题是apache poi如何为HSSF创建SUMPRODUCT公式。 SUMPRODUCT始终是数组函数。因此,SUMPRODUCT中嵌入的所有函数也应为数组函数。但是,apache poi不会将CLASS_ARRAY设置为SUMPRODUCT中嵌入的功能。而是将CLASS_VALUE设置为好像功能是独立的。

以下工作草案显示了此问题。有一种方法makeArrayFormula会将所有从FuncVarPtgCLASS_VALUE(函数变量解析内容)更改为CLASS_ARRAY。运行该方法后,SUMPRODUCT公式也可以在HSSF中按预期方式工作。

import java.io.FileOutputStream;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import org.apache.poi.hssf.record.CellValueRecordInterface;
import org.apache.poi.hssf.record.FormulaRecord;
import org.apache.poi.hssf.record.aggregates.FormulaRecordAggregate;
import org.apache.poi.ss.formula.ptg.Ptg;
import org.apache.poi.ss.formula.ptg.FuncVarPtg;

import java.lang.reflect.Field;

class CreateExcelFormula {

 static void makeArrayFormula(HSSFCell formulaCell) throws Exception {
  Field _record = HSSFCell.class.getDeclaredField("_record");
  _record.setAccessible(true); 
  CellValueRecordInterface recordInterface = (CellValueRecordInterface)_record.get(formulaCell);
  System.out.println(recordInterface);
  if (recordInterface instanceof FormulaRecordAggregate) {
   FormulaRecordAggregate formulaRecordAggregate = (FormulaRecordAggregate)recordInterface;
   FormulaRecord formulaRecord = formulaRecordAggregate.getFormulaRecord();
   Ptg[] ptgs = formulaRecord.getParsedExpression();
   for (Ptg ptg : ptgs) {
    if (ptg instanceof FuncVarPtg) {
     if (ptg.getPtgClass() == Ptg.CLASS_VALUE) {
      ptg.setClass(Ptg.CLASS_ARRAY);
     }
    }
   }
   formulaRecord.setParsedExpression(ptgs);
  }
  System.out.println(recordInterface);
 }

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

  try (
       Workbook workbook = new HSSFWorkbook(); FileOutputStream fileout = new FileOutputStream("Excel.xls") ) {
       //Workbook workbook = new XSSFWorkbook(); FileOutputStream fileout = new FileOutputStream("Excel.xlsx") ) {

   Sheet sheet = workbook.createSheet();
   Row row;
   Cell cell;

   row = sheet.createRow(0);

   cell = row.createCell(0);
   cell.setCellValue("F:");
   cell = row.createCell(1);
   //cell.setCellFormula("SUMPRODUCT((K:K=\"yes\")*(SUBTOTAL(103,OFFSET(K10,ROW(K:K)-MIN(ROW(K10:K10)),0))))");
   cell.setCellFormula("SUMPRODUCT((K10:K10000=\"yes\")*(SUBTOTAL(103,OFFSET(K10,ROW(K10:K10000)-ROW(K10),0))))");
   if (cell instanceof HSSFCell) {
    makeArrayFormula((HSSFCell)cell);
   }

   for (int r = 9; r < 30; r++) {
    row = sheet.createRow(r);
    cell = row.createCell(10);
    if (r % 2 == 0) cell.setCellValue("yes"); else cell.setCellValue("no");
   }

   for (int r = 14; r < 19; r++) {
    sheet.getRow(r).setZeroHeight(true);
   }  

   workbook.write(fileout);
  }

 }
}

Btw .:永远不要在数组公式中使用像K:K这样的完整列引用。这是一场表演夜马。 MIN周围的ROW(K10:K10)是多余的。

© www.soinside.com 2019 - 2024. All rights reserved.