UDF与Intersect运行缓慢

所以我正在创build一个函数来replace一些手动索引/匹配公式。 请注意,这个function的作品,但我的问题是速度。 所以我有一个数据透视表,6栏和约。 200.000行。 我想这find价值(我不使用pivotfunctions,这意味着这只是一个表的枢轴格式),我发现这运行速度比它在一个普通的数据表。 两者都将从SQL表中导入。

这个公式的一个部分立即运行,但是当我在同一张表中有几百个时,performance会变慢。

那么关于如何加快这一点的任何想法?

Function getnum2(ByVal Comp As String, Period As String, Measure As String, Optional BU As String, _ Optional Country As String, Optional Table As String, Optional TableSheet As String) As Double Dim pTable As PivotTable, wTableSheet As Worksheet If BU = "" Then BU = "Group" End If If Country = "" Then Country = "Total" End If If TableSheet = "" Then Set wTableSheet = Worksheets("Data") Else Set wTableSheet = Worksheets(TableSheet) End If If Table = "" Then Set pTable = wTableSheet.PivotTables("PivotTable1") Else Set pTable = wTableSheet.PivotTables(Table) End If 'Find match If Intersect(pTable.PivotFields("Bank").PivotItems(Comp).DataRange.EntireRow, _ pTable.PivotFields("Date").PivotItems(Period).DataRange.EntireRow, _ pTable.PivotFields("Business Unit").PivotItems(BU).DataRange.EntireRow, _ pTable.PivotFields("Country").PivotItems(Country).DataRange.EntireRow, _ pTable.PivotFields("Name").PivotItems(Measure).DataRange) Is Nothing Then getnum2 = "No match" ElseIf Intersect(pTable.PivotFields("Bank").PivotItems(Comp).DataRange.EntireRow, _ pTable.PivotFields("Date").PivotItems(Period).DataRange.EntireRow, _ pTable.PivotFields("Business Unit").PivotItems(BU).DataRange.EntireRow, _ pTable.PivotFields("Country").PivotItems(Country).DataRange.EntireRow, _ pTable.PivotFields("Name").PivotItems(Measure).DataRange).Count > 1 Then getnum2 = "More than 1 match" Else getnum2 = Intersect(pTable.PivotFields("Bank").PivotItems(Comp).DataRange.EntireRow, _ pTable.PivotFields("Date").PivotItems(Period).DataRange.EntireRow, _ pTable.PivotFields("Business Unit").PivotItems(BU).DataRange.EntireRow, _ pTable.PivotFields("Country").PivotItems(Country).DataRange.EntireRow, _ pTable.PivotFields("Name").PivotItems(Measure).DataRange) End If End Function 

而不是调用三次函数,你可以使用一个variables:

 Function getnum2(ByVal Comp As String, Period As String, Measure As String, Optional BU As String, _ Optional Country As String, Optional Table As String, Optional TableSheet As String) As Double Dim pTable As PivotTable, wTableSheet As Worksheet Dim rgResult as Range If BU = "" Then BU = "Group" End If If Country = "" Then Country = "Total" End If If TableSheet = "" Then Set wTableSheet = Worksheets("Data") Else Set wTableSheet = Worksheets(TableSheet) End If If Table = "" Then Set pTable = wTableSheet.PivotTables("PivotTable1") Else Set pTable = wTableSheet.PivotTables(Table) End If 'Find match Set rgResult = Intersect(pTable.PivotFields("Bank").PivotItems(Comp).DataRange.EntireRow, _ pTable.PivotFields("Date").PivotItems(Period).DataRange.EntireRow, _ pTable.PivotFields("Business Unit").PivotItems(BU).DataRange.EntireRow, _ pTable.PivotFields("Country").PivotItems(Country).DataRange.EntireRow, _ pTable.PivotFields("Name").PivotItems(Measure).DataRange) if rgResult Is Nothing Then getnum2 = "No match" ElseIf rgResult.Count > 1 Then getnum2 = "More than 1 match" Else getnum2 = rgResult.Value End If End Function 

一个非常简单的方法来实现这一点是通过使用两个数据透视表。

  • 在数据透视表1中,将要返回的数字的所有字段放在ROWS区域中,并将要返回的字段放在VALUES区域中,并将聚合设置为COUNT。
  • 在数据透视表2中,将所有字段,而不是你想要返回的数字,在ROWS区域,并把你想要返回的字段,在汇总设置为SUM或MIN或MAX(哪个并不重要) 。

然后,您可以使用一个参数化GETPIVOTDATA函数来检查数据透视表1,以查看您正在查找的内容是否是唯一的(即COUNT = 1),如果是,则查找数据透视表2中该项目的SUM / MIN / MAX。 鉴于该项目是唯一的,那么SUM / MIN / MAX只在一个数字上运行,所以没有任何结果。

以下是使用简化数据的外观:

在这里输入图像说明

我已经向两个枢轴添加了条件格式,以突出显示我们想要返回“多个项目”文本的多个事件,并且正如您所看到的那样,填充“查找”表第6列的公式只会返回唯一项你的要求。

这里是公式,使用Table notation,因为我的Lookup范围已经变成了Excel Table:

=IF(GETPIVOTDATA("6",$A$3,"1",[@1],"2",[@2],"3",[@3],"4",[@4],"5",[@5])=1,GETPIVOTDATA("6",$H$3,"1",[@1],"2",[@2],"3",[@3],"4",[@4],"5",[@5]),"Multiple Items")

如果我在查找表中随机input单元格,可以看到当某些项目不在数据透视表中时会发生什么情况:

在这里输入图像说明

这种方法是可行的,因为您要返回的字段是数字字段,这意味着您可以将其添加到Pivot的VALUES窗格中。 但是,您仍然可以使用它来返回string,方法是向源数据(例如行号)添加唯一的ID,然后将其放入VALUES字段,然后使用双GETPIVOTDATA查找来检索它,并使用它来检索关联的string在源数据中。

另一种方法是使用合适的分隔符(如pipe道字符)简单地将列连接成主键 然后将其用作查找键。 如果你在分类后的数据上进行二分search,这将是闪电般的。 (我在http://dailydoseofexcel.com/archives/2015/04/23/how-much-faster-is-the-double-vlookup-trick/ )讨论这个问题。 不好的一面是,如果有多个项目,你不会被警告。 但是可以使用第一个返回的匹配位置进行第二次查找,以查看是否获得另一个结果,如果是,则返回“多个项目”。 这将是超快的。

这是最快的方法:在已sorting的查找表上使用二进制匹配。 在左边,我有5列x 1048575行1到10之间的随机数。这些已连接在列G中作出一个非唯一键,然后按该键上升sorting。

在这里输入图像说明

(因为连接的键是文本,它从左到右按字母顺序sorting,这就是1 | 1 | 1 | 1 | 1和1 | 1 | 1 | 1 | 2之间出现1 | 1 | 1 | 1 | 10的原因)

我给列G中的数据命名了Concat的范围,以简化公式。 J2中的查找公式返回查找项目的行号,当且仅当该项目对数据集唯一时。 公式是:

 =IF(OR( AND(INDEX(Concat,MATCH(I2,Concat,1))=I2, MATCH(I2,Concat,1)=1), AND(INDEX(Concat,MATCH(I2,Concat,1))=I2,INDEX(Concat,MATCH(I2,Concat,1)-1)<>I2)),MATCH(I2,Concat,1),NA()) 

对于查询表1048576行,这在一个实例中以0.01毫秒执行。 我的双GETPIVOTDATA方法上面花了6毫秒。 所以你有它:一个复杂的公式,可以提高600倍的效率。

我可以在后面解释公式,但需要注意的是,其中一些复杂性是由于边缘情况,您可能会在第一行中出现一个独特的项目。如果我忽略该边缘情况,则公式如下:

 =IF( AND(INDEX(Concat,MATCH(I3,Concat,1))=I3,INDEX(Concat,MATCH(I3,Concat,1)-1)<>I3),MATCH(I3,Concat,1),NA())