作者heavendemon ()
看板Office
標題[問題] sumifs多條件 VBA陣列
時間Tue Mar 28 22:15:34 2017
軟體:excel
版本:2010
爬了一下文,發現之前so大的資料已經不在dropbox了QQ
因為用了函數發現嚴重影響計算效率
我原始資料(sheet1)只要一更新,其他工作頁上的函數就會重新計算
導致我原始資料每輸入一筆資料就耗費快一分鐘在計算函數上,函數如下
=IF(SUMIFS(sheet1!J:J,sheet1!B:B,A3,sheet1!C:C,B3)<H3,"未完成","完成")
因此想到用VBA設置按鈕讓需要計算的時候按下按鈕即可,程式碼如下
Set rngpo = Sheets(1).Range("b1:b" & lstrow)
Set rngno = Sheets(1).Range("c1:c" & lstrow)
Set rngout = Sheets(1).Range("j1:j" & lstrow)
With ActiveSheet
myrow = .Range("b3").End(xlDown).Row
For i = 3 To myrow
If Application.SumIfs(rngout, rngpo, Cells(i, 1).Value, rngno, Cells(i,
2).Value) < Cells(i, 8).Value Then
Cells(i, 13).Value = "未完成"
Else: Cells(i, 13).Value = "完成"
End If
Next i
後來發現按下按鈕後還是非常沒有效率,平均100rows的資料要25秒
自己在網上搜尋後,發現使用陣列會加速很多
但對VBA完全新手的我 array 的使用方式研究好久還是不太清楚
找到使用陣列的優化程式碼如下
Sub sumif()
Const n& = 50000
Dim d As Object, a, u&(), i As Long
Set d = CreateObject("scripting.dictionary")
a = Range("A1:B" & n)
ReDim u(1 To n, 1 To 1)
For i = 1 To n
d(a(i, 1)) = d(a(i, 1)) + a(i, 2)
Next i
For i = 1 To n
u(i, 1) = d(a(i, 1))
Next i
Range("E1:E" & n) = u
End Sub
原本函數的sample如下
=RANDBETWEEN(10,99) in A1:A50000
and
=RANDBETWEEN(50,500000) in B1:B50000
Then in C1
=SUMIF(A:A,A1,B:B)
我是完全不懂他在哪個地方有做加總的動作
不知道哪位大大可以看出這個外國人的邏輯
最後同場加映似乎更快的方法,這個我比較看得懂(因為沒有陣列)
但我找不到他的criteria他只合併了criteria range 成為另外一個range
但是他的criteria在哪?
還有他用排序的方式去加總,不是應該要在合併完AB欄位後就要先排序一次嗎?
太多疑問不知道有沒有大神可以教學陣列的邏輯(願意付學費)
Sub FasterThanSumifs()
'FasterThanSumifs Concatenates the criteria values from columns A and B -
'then uses simple IF formulas (plus 1 sort) to get the same result as a
sumifs formula
'Columns A & B contain the criteria ranges, column C is the range to sum
'NOTE: The data is already sorted on columns A AND B
'Concatenate the 2 values as 1 - can be used to concatenate any number of
values
With Range("D2:D25001")
.FormulaR1C1 = "=RC[-3]&RC[-2]"
.Value = .Value
End With
'If formula sums the range-to-sum where the values are the same
With Range("E2:E25001")
.FormulaR1C1 = "=IF(RC[-1]=R[-1]C[-1],RC[-2]+R[-1]C,RC[-2])"
.Value = .Value
End With
'Sort the range of returned values to place the largest values above the
lower ones
Range("A1:E25001").Sort Key1:=Range("D1"), Order1:=xlAscending, _
Key2:=Range("E1"), Order2:=xlDescending, Header:=xlYes
Sheet1.Sort.SortFields.Clear
'If formula returns the maximum value for each concatenated value match &
'is therefore the equivalent of using a Sumifs formula
With Range("F2:F25001")
.FormulaR1C1 = "=IF(RC[-2]=R[-1]C[-2],R[-1]C,RC[-1])"
.Value = .Value
End With
End Sub
第一次發文 如果排版有問題請告知
--
※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 47.89.55.16
※ 文章網址: https://webptt.com/m.aspx?n=bbs/Office/M.1490710537.A.C9F.html
※ 編輯: heavendemon (47.89.55.16), 03/28/2017 22:18:31
※ 編輯: heavendemon (47.89.55.16), 03/28/2017 22:20:37
1F:→ soyoso: 以a(i,1),a欄的值做為d的索引值,並於d(a(i,1))=d(a(i,1) 03/29 00:02
2F:→ soyoso: )+a(i,2)做累加,a(i,2)為b欄 03/29 00:03
3F:→ soyoso: 第二個為於range("e2:e25001")處以公式=if(d2=d1,c2+e1,c2 03/29 00:23
5F:→ soyoso: 將累加的最後一筆,以排序方式d欄小至大,e欄大至小移至分 03/29 00:29
6F:→ soyoso: 組的第一筆 03/29 00:29
7F:→ soyoso: 原文有寫到"不是應該要在合併完AB欄位後就要先排序一次" 03/29 00:32
8F:→ soyoso: 應是備註處已有寫到The data is already sorted on 03/29 00:33
9F:→ soyoso: columns A AND B的原因,故巨集內無再多加入 03/29 00:34
10F:→ soyoso: 個人覺得下方巨集不一定會輸出正確,例如a欄9,b欄99合併 03/29 00:37
11F:→ soyoso: 為999,a欄99,b欄9,合併也是999 03/29 00:38
12F:→ soyoso: 就會加總起來了,這和sumifs的判斷上不同的 03/29 00:39
14F:→ heavendemon: 謝謝so大 指點 關於陣列的部分 小弟我實測後結果也是 03/29 00:44
15F:→ heavendemon: 和sumif函數結果不同 我用監看陣列後也看不明白 03/29 00:44
16F:→ heavendemon: a(1,1)並不是A1的值 不過是有出現在A欄 但a(1,2)的值 03/29 00:46
17F:→ heavendemon: 不是B1外 根本沒有出現在原本的range裡 是小弟理解錯 03/29 00:46
18F:→ heavendemon: 誤還是這個陣列本身就有問題 如果so大 方便 可否用陣 03/29 00:47
19F:→ heavendemon: 列示範 sumifs的寫法 如果是要兩個criteria就會變成 03/29 00:49
20F:→ heavendemon: 三欄的陣列? a(i,1),(i,2),(i,3) 03/29 00:49
21F:→ heavendemon: 或有任何加速sumifs在VBA的方法 幾千筆跑下來很費時 03/29 00:58
22F:→ heavendemon: 另外我發現這兩個方法應該都是用本身欄位中的值當作 03/29 01:14
23F:→ soyoso: 如a指定以a1:b1起的範圍的話a(1,1)應是a1的值,a(1,2)為b1 03/29 01:15
25F:→ heavendemon: criteria 但我的函數是將跨工作頁的cell當criteria 03/29 01:16
26F:→ heavendemon: 所以才不能用樞紐分析 不知道這樣是不是還可以用陣列 03/29 01:16
27F:→ heavendemon: 的方式執行出sumifs的效果 03/29 01:17
29F:→ soyoso: 其他字元來區別,再將d欄排序的話,是否可以排除回文內所 03/29 01:28
30F:→ soyoso: 提到的問題呢? 03/29 01:29
31F:→ soyoso: 如要以上方巨集改為sumifs的話,可以 03/29 01:37
33F:→ soyoso: 函數做為比對而已 03/29 01:38
34F:→ heavendemon: 所以在dictionary裡面 前面一定是key 後面就是item 03/29 10:34
35F:→ heavendemon: d(a(i,1)&"_"&a(i,2))就是一定是對應到a(i,3) 03/29 10:37
36F:→ heavendemon: 不知道我這樣理解有沒有錯 非常感謝so大這麼晚還解答 03/29 10:38
37F:→ soyoso: 理解上會以d(a(i,1)&"_"&a(i,2))會對應到a(i,1)&"_"&a(i,2 03/29 10:51
38F:→ soyoso: )並將a(i,3)累加進去,看是否也和原po回文的理解上相同 03/29 10:52
39F:→ heavendemon: 了解 另外我要將不連續的range放到陣列裡面 先使用 03/29 11:38
40F:→ heavendemon: application.union 再丟進陣列 但陣列只讀取到第一個 03/29 11:39
41F:→ heavendemon: range 陣列一定要連續range沒有其他辦法?或是另開一 03/29 11:40
42F:→ heavendemon: 陣列去除存另外的range ? 03/29 11:41
43F:→ soyoso: 不連續方式想到的是迴圈或以Array的方式 03/29 12:03
※ 編輯: heavendemon (47.89.55.16), 03/29/2017 15:14:04
45F:→ heavendemon: on error resume next 之後在B欄某一criteria的結果 03/29 16:03
46F:→ soyoso: 有錯誤訊息? 03/29 16:03
47F:→ heavendemon: 是錯的 不過是有順利跑完 就是16140.41這個crieria 03/29 16:04
48F:→ heavendemon: 結果和sumifs不同 03/29 16:05
49F:→ heavendemon: 錯誤是型態不符合 03/29 16:06
50F:→ soyoso: sumarr方面是否為文字類型呢? 03/29 16:16
52F:→ soyoso: d內,如又有符合a、b欄時,進行累加上就會出現型態不符合 03/29 16:19
54F:→ heavendemon: 比對後,速度反而更慢 平均100row40秒 這個比對的部 03/29 18:55
55F:→ heavendemon: 有沒有比較聰明效率的寫法 感覺繞了一大圈回到原點.. 03/29 18:56
56F:→ soyoso: 將判斷的結果先以丟進變數array內,迴圈結束後再一次,以 03/29 19:24
57F:→ soyoso: range=變數的方式寫入 03/29 19:25
58F:→ soyoso: 寫入上應會用到工作表函數transpose 03/29 19:28
59F:→ heavendemon: 小弟對陣列真的不熟悉 不知道能不能有簡單的示範參考 03/29 20:10
60F:→ soyoso: 原文上面的巨集內Range("E1:E" & n) = u和上面的迴圈,就 03/29 20:51
61F:→ soyoso: 約是回文內的意思 03/29 20:51
非常感謝so 大不厭其煩解答
我最後用了錄製巨集的方式取得原本sumifs函數的formulaR1C1格式
直接將R1C1的函數丟到指定的range範圍
最後把函數取代成值
達到每100rows低於一秒的效率
花了很久的時間 才回頭發現最簡單的方法
希望能給有遇到函數公式太多導致原始資料更新耗時的朋友
一些參考和幫助
※ 編輯: heavendemon (47.89.55.16), 03/30/2017 18:10:19