Excel VBA 使用 SUMPRODUCT 和 COUNTIFS - 速度问题
Posted
技术标签:
【中文标题】Excel VBA 使用 SUMPRODUCT 和 COUNTIFS - 速度问题【英文标题】:Excel VBA using SUMPRODUCT and COUNTIFS - issue of speed 【发布时间】:2016-09-19 06:08:19 【问题描述】:我有速度问题。 (为长篇道歉……)。我在 Windows 上使用 Excel 2013 和 2016。
我有一个工作簿,它在 200,000 个单元格表(1000 行 x 200 列)上执行 10,000 多次计算。
每个计算都返回一个整数(例如过滤行的计数)或更常见的百分比(例如过滤行的值的总和除以行的值的总和)。计算结构是SUMPRODUCT(COUNTIFS())
思想的变体,大致如下:
=IF($B6=0,
0,
SUMPRODUCT(COUNTIFS(
Data[CompanyName],
CompanyName,
Data[CurrentYear],
TeamYear,
INDIRECT(VLOOKUP(TeamYear&"R2",RealProgress,2,FALSE)),
"<>"&"",
Data[High Stage],
NonDom[NonDom]
))
/$B6
)
以上解释:
-
Data[Company Name] 和 CompanyName 这对是表中的列,也是第一个过滤器的条件值。
Data[Current Year] 和 TeamYear 这对同上,构成第二个过滤器。
第三对查找中间表并返回列名,条件(
"<>"&""
)为“非空白”,即返回在该列中有值的所有行
最后,第四对与上面的 3 类似,但返回一组与 中的值集相匹配的值
最后,四个过滤器用 AND 语句连接在一起。
需要注意的是,在所有计算中,使用 SUMPRODUCT(COUNTIFS())
的原则相同——但是这个主题有很多变化。
目前,在选定范围的工作表上使用“计算”(而不是较慢地计算整个工作簿),计算速度约为 30-40 秒。还不错,而且可以忍受,因为并非一直都在执行计算。
不幸的是,该模型将被扩展,现在可能接近 20,000 行而不是 1,000 行。计算性能与行数或单元格数直接相关,因此我预计性能会直线下降!
显而易见的解决方案 [1] 是使用数组,理想情况下将保存在内存中的数组传递给单元格中的公式,然后将其与过滤器及其条件一起处理(查找过滤器也是数组)。
替代解决方案 [2] 是使用数组编写 UDF,但在互联网上阅读的意见是 UDF 比原生 Excel 函数慢得多。
两个问题:
-
解决方案 [1] 是否可行,最好的方法是,如果可以,我将如何构建它?
如果解决方案 [1] 不可行或不是最好的方法,是否有人认为解决方案 [2] 与我当前的解决方案相比会快多少?
还有其他更好的解决方案吗?我了解 Power BI Desktop、PowerPivot 和 PowerQuery,但这是供非 Excel 用户使用的商业应用程序,需要以当前 Excel 行和列的“网格”形式呈现。
非常感谢您的阅读!
附录:我将尝试为 Worksheet.Activate 事件中的每个工作表运行一个数组计算,看看是否可以节省一些时间。
【问题讨论】:
IMO 这个问题不可能提供一个好的答案。有太多的事情可能 有帮助,但如果没有更多信息和最好的工作簿副本,就不可能肯定地说。例如,数据透视表可能是一个很好的解决方案。用二进制搜索版本或 INDEX 和 MATCH 替换您的 VLOOKUP 可能会有所帮助。在某些情况下,如果 UDF 允许您对某些处理进行短路,那么 UDF 可以比公式更快。将源数据中的最后一个条件转换为 TRUE/FALSE 公式可能会有所帮助。正如我所说,太多的选择。 ;) 谢谢罗里。我尝试过 INDEX MATCH 而不是 VLOOKUP 但速度较慢。我也尝试了 '2 VLOOKUPs' 解决方案 - 但这也不起作用。枢轴不是答案,因为我需要对输出进行严格控制和良好的格式化。不过,我会考虑一下 TRUE/FALSE 的想法……听起来很有趣!对于其他任何人,我正在寻找一般性的指示和建议,因为我明白具体的解决方案将取决于我! 就像我说的那样,有太多的可能性...很少有解决性能问题的绝对方法。您可以投资 FastExcel 以确定瓶颈的确切位置。 (INDIRECT 显然无济于事) 我使用 INDIRECT 以便可以将公式复制到每张纸上的数百个整个网格中。从理论上讲,我可以替换每个公式,但这需要很长时间,如果公式发生变化,那么我将在工作簿中更改数千个公式。不过我会看看 FastExcel!谢谢! 【参考方案1】:如果希望提高速度,将数据写入数组通常是一个好主意。这样做:
Dim myTable As ListObject
Dim myArray As Variant
'Set path for Table variable
Set myTable = ActiveSheet.ListObjects("Table1")
'Create Array List from Table
myArray = myTable.DataBodyRange
(Source)
【讨论】:
以上是关于Excel VBA 使用 SUMPRODUCT 和 COUNTIFS - 速度问题的主要内容,如果未能解决你的问题,请参考以下文章
自动填充 Application.Countifs.Formula VBA Excel
Excel:其他字段中使用的动态范围日期:Sumproduct