2016-09-19 157 views
0

我有一個速度問題。 (道歉爲長期職位...)。我正在使用Excel 2013和2016 for Windows。Excel VBA使用SUMPRODUCT和COUNTIFS - 速度問題

我有一個工作簿,在200,000個單元格表(1000行×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 
) 

以上解釋:

  1. 該對數據[公司名稱]和公司名稱在表中與列第一個過濾器的條件值。
  2. 數據[當前年份]和TeamYear與上面相同,構成第二個過濾器。
  3. 第三對查找中間表並返回該列的名稱,條件("<>"&"")爲'非空白',即返回在該列中具有值的所有行
  4. 最後,第四對是類似於上面的3,但返回一組與
  5. 中的值相匹配的值。最後,四個過濾器用AND語句連接在一起。 重要的是要注意,在所有的計算中,使用SUMPRODUCT(COUNTIFS())的原理是相同的 - 但是這個主題有很多變化。 目前,使用計算選定範圍的工作表(而不是計算整個工作簿的速度較慢),可以計算出大約30-40秒的計算速度。不壞,並且可以忍受,因爲計算不會一直執行。

不幸的是,該模型將被擴展,現在可能接近20000行而不是1000行。計算性能與行數或單元格數量直接相關,因此我預計性能會直線下降!

顯而易見的解決方案[1]是使用數組,理想情況下將數組保存在內存中,然後將其與過濾器及其條件(查找過濾器也是數組)一起處理。

另一種解決方案是使用數組編寫UDF,但在互聯網上閱讀的意見是,UDF比本地Excel函數慢得多。

兩個問題:

  1. 是解決方案[1]可能的,而且這樣做的最佳方式,如果是這樣我將如何構建呢?
  2. 如果解決方案[1]不可能或不是最好的方法,沒有人有任何想法可以比我的當前解決方案更快的解決方案[2]?
  3. 還有其他更好的解決方案嗎?我瞭解Power BI Desktop,PowerPivot和PowerQuery - 但這是一個供非Excel用戶使用的商業應用程序,需要以當前Excel「網格」形式的行和列顯示。

非常感謝您的閱讀!

附錄:我將嘗試爲Worksheet.Activate事件上的每個工作表運行一次數組計算,並查看是否節省了一些時間。

+1

IMO這個問題是不可能提供一個很好的答案。只有太多的東西可能會幫助你,但是如果沒有更多的信息和工作手冊的副本,這是不可能的。例如,數據透視表可能是一個很好的解決方案。用二進制搜索版本或INDEX和MATCH代替VLOOKUP可能會有所幫助。在某些情況下,如果UDF允許短路某些處理,則它們可能比公式更快。在源數據中將最後一個條件轉換爲TRUE/FALSE公式可能會有所幫助。正如我所說的,選擇太多了。 ;) – Rory

+0

謝謝羅裏。我試過INDEX MATCH而不是VLOOKUP,但速度較慢。我也嘗試了'2 VLOOKUP'解決方案 - 但是這也不起作用。 樞軸不是一個答案,因爲我需要密切控制和良好的輸出格式。 我會思考真實/錯誤的想法,雖然......聽起來有趣! 對於其他人,我正在尋找一般指針和建議,因爲我明白特定的解決方案將歸於我! – WithnailsHoliday

+0

就像我說的,太多的可能性......性能問題很少有絕對的治療方法。您可能會投資FastExcel以確定瓶頸的確切位置。 (INDIRECT顯然不會起到幫助作用) – Rory

回答

0

如果想提高速度,將數據寫入數組通常是個好主意。做過這樣的:

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