=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是实际数据开始的单元格。此单元格上方的行包含标题。
我可以使用HSSF
确认这是一个问题。但这与公式重新计算无关。 OFFSET
本身和NOW
一样易失。即使不设置setForceFormulaRecalculation(true)
,两个强制公式也会重新计算。但是在二进制*.xls
文件中,ROW(K:K)
中的OFFSET
最初不评估为数组。因此,仅对1
(ROW(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
会将所有从FuncVarPtg
的CLASS_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)
是多余的。