【问题标题】:Treat a formula like any other text value when exporting to Excel导出到 Excel 时,像对待任何其他文本值一样对待公式
【发布时间】:2015-11-05 20:26:02
【问题描述】:

我已经构建了一个可以将数据导出为 Excel 的报告解决方案。使用 PHPExcel,这项工作非常漂亮。但是,我们需要构建仪表板样式模板表并在运行时将它们插入到输出中,包括公式。我的所有这些工作都很好,我的问题是有时这些仪表板模板相当复杂,PHPExcel 在尝试输出时会阻塞公式。

我花了相当多的时间寻找解决方法,甚至到了forking my own branch 并添加了一种禁用计算的方法,该方法会试图阻止公式被处理,但无法让它工作。我也试过禁用计算缓存。

@MarkBaker 在这个库上做得非常出色,我多次看到他提到这不是为修改文件而设计的,但如果做得非常仔细,它似乎可以工作。

我的问题是,如何告诉 PHPExcel 停止 尝试计算公式?我希望它像对待任何其他文本值一样对待它们,并让 Excel 自己处理验证。这可能吗?

例如,下面是一个 PHPExcel 似乎不喜欢的公式:

=IFERROR(IF($L$3="UNITS",COUNTIF('K1_Current_Accounts'!$M$2:$M$10000,'Snapshot Summary'!H5)+COUNTIF('K1_Current_Available'!$M$2:$M$10000,'Snapshot Summary'!H5),IF($L$3="Cost Basis",SUMIF('K1_Current_Accounts'!$M$2:$M$10000,'Snapshot Summary'!H5,'K1_Current_Accounts'!$E$2:$E$10000)+SUMIF('K1_Current_Available'!$M$2:$M$10000,'Snapshot Summary'!H5,'K1_Current_Available'!$E$2:$E$10000),IF($L$3="Notes",SUMIFS('K1_Current_Accounts'!$AA$2:$AA$10000,'K1_Current_Accounts'!$M$2:$M$10000,'Snapshot Summary'!H5,'K1_Current_Accounts'!$R$2:$R$10000,'Snapshot Summary'!$A$7)+SUMIFS('K1_Current_Accounts'!$AA$2:$AA$10000,'K1_Current_Accounts'!$M$2:$M$10000,'Snapshot Summary'!H5,'K1_Current_Accounts'!$R$2:$R$10000,'Snapshot Summary'!$A$8)+SUMIFS('K1_Current_Accounts'!$AA$2:$AA$10000,'K1_Current_Accounts'!$M$2:$M$10000,'Snapshot Summary'!H5,'K1_Current_Accounts'!$R$2:$R$10000,'Snapshot Summary'!$A$9),IF($L$3="Cash",SUMIFS('K1_Historical_Accounts'!$AC$2:$AC$10000,'K1_Historical_Accounts'!$M$2:$M$10000,'Snapshot Summary'!H5,'K1_Historical_Accounts'!$R$2:$R$10000,'Snapshot Summary'!$A$10))))),"")

我读到SUMIFS 可能会导致问题,但我不知道如何按照建议将这些重构为SUMPRODUCT(这些仪表板由业务开发团队构建)。此外,我们将列的范围设置为 10000,因为像 A:A 这样的范围也不起作用。它们在 Excel 中运行良好,所以如果我能让 PHPExcel 停止尝试验证所有内容并让它通过,我的生活将是辉煌的!

编辑

进一步查看堆栈跟踪,它似乎在尝试编写图表时开始发生:

编辑 2

查看图表生成作为这个问题的根源,只有一个图表,它的数据源是='Sales History'!$Y$4:$CR$5,我可以看到它肯定会尝试处理,但这不应该引起问题,因为它是一个比较简单的参考。

【问题讨论】:

  • 注意,目前正在为 PHPExcel 实现 SUMIFS() 函数
  • 非常感谢马克!

标签: php excel phpexcel


【解决方案1】:

您可以通过将 Writer 的 preCalculateFormulas 标志设置为 false 来防止 PHPExcel 在编写时计算公式

$objWriter->setPreCalculateFormulas(false);

编辑

我对 Excel2007 Writer 进行了一些实验性更改,以禁用图表的单元格计算:

/PHPExcel/Writer/Excel2007/Chart.php 我添加了一个新属性

protected $calculateCellValues;

并修改了writeChart() 方法以接受额外的参数:

public function writeChart(PHPExcel_Chart $pChart = null, $calculateCellValues = true)
{
    $this->calculateCellValues = $calculateCellValues;

并将后续调用修改为$pChart->refresh()

//    Ensure that data series values are up-to-date before we save
if ($this->calculateCellValues) {
    $pChart->refresh();
}

/PHPExcel/Writer/Excel2007.phpsave() 方法中,我将第300 行附近对writeChart() 的调用修改为:

$objZip->addFromString(
    'xl/charts/chart' . ($chartCount + 1) . '.xml', 
    $this->getWriterPart('Chart')
        ->writeChart($chart, $this->preCalculateFormulas)
);

(即,我已将$this->preCalculateFormulas 注入图表编写器。)

一项临时测试表明,当打开生成的 xlsx 文件时,MS Excel 将自行触发重新计算....但这还不是一个完整的测试

【讨论】:

  • 我试过了。它默认为false,也在PHPExcel\Writer\Excel2007 第39 行。在那个特定的作者(我需要的那个)中,它在第290 行将这个值传递给工作表,但仍然试图验证公式。我正在尽我所能避免编写自己的库来处理这些东西,因为它非常复杂(如你所知),但我没有取得太大进展。让我看看是否可以在此处找到仍在运行公式计算的代码路径。该函数会停止图表中的计算吗?
  • 您是否在使用列自动调整大小?这应该只触发可以覆盖 preCalculateFormulas false 的公式计算
  • 否,但我正在使用自动过滤。我发布了对我的问题的编辑,并在堆栈跟踪中使用了更下方的屏幕截图。似乎图表生成是触发一系列事件的原因,从而导致计算公式。
  • AutoFiltering 不使用计算 IIRC,尽管我已经有一段时间没有查看那部分代码了;但图表确实......虽然我想我会在图表中禁用它......我会检查代码,看看我是否遗漏了任何东西
  • 看起来writeChart 为图表触发了refresh() 方法,然后为 PlotArea、DataSeries、DataSeriesValues 触发相同的方法。在 PHPExcel_Chart_DataSeriesValues 中,refresh() 方法运行 $calcEngine->_calculateFormulaValue(...)$pCell 值从 A1 开始。然后将工作表设为$pCellParent,然后最终对堆栈进行操作并检查一些公式。如果不花更多时间试图理解这一切,我很难理解。
【解决方案2】:

我从未使用过 PHPExcel,但我可以为您将该公式从 SUMIF/S() 改写为 SUMPRODUCTS。您可以使用 Alt + Enter 使这些庞大的公式更具可读性。

为了了解 sumproduct,当应用任何数学运算时,Excel 将 TRUE/FALSE 视为 1/0,因此您可以 {TRUE,TRUE,FALSE} x {FALSE,TRUE,TRUE} 得到 {0 ,1,0}。然后 SUMPRODUCT 获取 {0,1,0} 并将其乘以您尝试过滤的行/列总数,然后对这些值求和。

=IFERROR(
IF($L$3="UNITS",
COUNTIF('K1_Current_Accounts'!$M$2:$M$10000,'Snapshot Summary'!H5)
+COUNTIF('K1_Current_Available'!$M$2:$M$10000,'Snapshot Summary'!H5),
IF($L$3="Cost Basis",
SUMPRODUCT(--('K1_Current_Accounts'!$M$2:$M$10000='Snapshot Summary'!H5),'K1_Current_Accounts'!$E$2:$E$10000)
+SUMPRODUCT(--('K1_Current_Available'!$M$2:$M$10000='Snapshot Summary'!H5),'K1_Current_Available'!$E$2:$E$10000),
IF($L$3="Notes",
SUMPRODUCT('K1_Current_Accounts'!$AA$2:$AA$10000,--('K1_Current_Accounts'!$M$2:$M$10000='Snapshot Summary'!H5),--('K1_Current_Accounts'!$R$2:$R$10000='Snapshot Summary'!$A$7))
+SUMPRODUCT('K1_Current_Accounts'!$AA$2:$AA$10000,--('K1_Current_Accounts'!$M$2:$M$10000='Snapshot Summary'!H5),--('K1_Current_Accounts'!$R$2:$R$10000='Snapshot Summary'!$A$8))
+SUMPRODUCT('K1_Current_Accounts'!$AA$2:$AA$10000,--('K1_Current_Accounts'!$M$2:$M$10000='Snapshot Summary'!H5),--('K1_Current_Accounts'!$R$2:$R$10000='Snapshot Summary'!$A$9)),
IF($L$3="Cash",
SUMPRODUCT('K1_Historical_Accounts'!$AC$2:$AC$10000,--('K1_Historical_Accounts'!$M$2:$M$10000='Snapshot Summary'!H5),--('K1_Historical_Accounts'!$R$2:$R$10000='Snapshot Summary'!$A$10))))))
,"")

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-03
    • 2018-03-12
    • 2013-04-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多