程式可以執行,但資料有好幾千筆,電腦跑了20分鐘還是沒結果(白畫面),尋求各位的協助完成資料!!
先講想要改的程式碼:
總共有兩張表分別是資料的aaa與資料輸出的表,aaa有3行分別是type(有三種FCST、SO和Ship)、產品ID、值;資料輸出的表有三行,分別是產品ID、N或公司名、要輸出值的行
aaa!A:A= """ & cate1 & """ 第一個條件,即FCST、SO或Ship
aaa!B:B= """ & Bnum & """  第二個條件,產品ID
aaa!E:E 尋找的值
ifna(,0) 因為會有空值無法比較,故讓空值為0
Application.Evaluate("=ifna(Lookup(1,0/((aaa!A:A= """ & cate1 & """ ) * (aaa!B:B= """ & Bnum & """ )), aaa!E:E),0)")
用for迴圈在輸出表由第二筆ID順著往下找,跑到最後一筆資料為止
ex: 若for到4,尋找輸出表中第一行第四列的ID並與表aaa第二行比對,a得到表aaa的FCST值、b得到表aaa的SO值、c得到表Ship的FCST值,當得到FCST、SO和Ship的值後會進行比較,若輸出表第三行相對的值為N,則會再做比對,若不是則直接傳a也就是FCST的值到輸出表的相對應的第四行。
完整程式碼
i = 1
cate1 = "FCST"   
cate2 = "SO"
cate3 = "Ship"
row1 = Cells(Rows.Count, 1).End(xlUp).Row
'Debug.Print row1
Do Until i >= row1
i = i + 1
Debug.Print i
Bnum = Range("a" & i)
a = Application.Evaluate("=ifna(Lookup(1,0/((aaa!A:A= """ & cate1 & """ ) * (aaa!B:B= """ & Bnum & """ )), aaa!E:E),0)")
b = Application.Evaluate("=ifna(Lookup(1,0/((aaa!A:A= """ & cate2 & """ ) * (aaa!B:B= """ & Bnum & """ )), aaa!E:E),0)")
c = Application.Evaluate("=ifna(Lookup(1,0/((aaa!A:A= """ & cate3 & """ ) * (aaa!B:B= """ & Bnum & """ )), aaa!E:E),0)")
If Cells(i, 3) = "N" Then
If a > b + c Then
Cells(i, 4) = b + c
Else
Cells(i, 4) = a
End If
Else
Cells(i, 4) = a
End If
Loop
測試檔:https://www.mediafire.com/file/u0e4booej78jhdr/Test.xlsx/file
補充:FCST、SO、Ship一個ID只會各出現一次,只會少不會多,除非ID相同公司卻不同
這是 Libre Office Calc 可以跑的程式碼,應該可以轉成 Excel 用的
轉換規則我想應該是這樣:
ThisComponent.getCurrentController.activesheet => ActiveSheet
ThisComponent.Sheets.getByName("aaa") => Sheets("aaa")
.getCellByPosition(x, i).string => .Cells(i+1, x+1).Value
.getCellByPosition(x, i).Value => .Cells(i+1, x+1).Value
t <> "" ,我不確定 Excel 會怎麼判斷,還是應該要用 Nothing 來判斷,可能也要改一下Sub Main
	Dim curSheet, dataSheet
	curSheet = ThisComponent.getCurrentController.activesheet
	dataSheet = ThisComponent.Sheets.getByName("aaa")
	
	'先掃描目前資料表(輸出資料表),先建立字典Dict key => index(0開始算)
	Dim dict, count, i, t
	Set dict = CreateObject("Scripting.Dictionary")
	i = 1
	count = 0
	t = curSheet.getCellByPosition(0, i).String
	key = t & "/" & curSheet.getCellByPosition(1, i).string
	while t <> ""
		dict.Add key, count
		count = count + 1
		i = i + 1
		t = curSheet.getCellByPosition(0, i).String
		key = t & "/" & curSheet.getCellByPosition(1, i).String
	wend
	
	'建立陣列Arr,共有 count*3 個 Double 元素(因為有三種)
	Dim arr(count*3) As Double
	
	'開始掃描資料檔,如果符合 key 則存入 Arr[Dict[key]*3+type]
	Dim idx, tpy, val
	i = 1
	t = dataSheet.getCellByPosition(1, i).String
	key = t & "/" & dataSheet.getCellByPosition(3, i).string
	while t <> ""
		if dict.Exists(key) then
			idx = CInt(dict.Item(key))*3
			tpy = dataSheet.getCellByPosition(0, i).String
			val = dataSheet.getCellByPosition(4, i).Value
			if tpy = "FCST" then
				arr(idx) = arr(idx) + val
			elseif tpy = "SO" then
				arr(idx+1) = arr(idx+1) + val
			elseif tpy = "Ship" then
				arr(idx+2) = arr(idx+2) + val
			else
			endif
		endif
		i = i + 1
		t = dataSheet.getCellByPosition(1, i).String
		key = t & "/" & dataSheet.getCellByPosition(3, i).string
	wend
	
	'將 Arr 寫入到目前資料表
	Dim a,b,c
	for i=1 to count
		idx=(i-1)*3
		a=arr(idx)
		b=arr(idx+1)
		c=arr(idx+2)
		if curSheet.getCellByPosition(2, i).String = "N" And a > b + c then
			t=b+c
		else
			t=a
		endif
		curSheet.getCellByPosition(3, i).Value=t
		'curSheet.getCellByPosition(4, i).String= a & "/" & b & "/" & c
	next i
End Sub
Dim arr(count*3) As Double
顯示count要是常數,該怎麼轉換呢?
試試看這樣可不可以
Dim arr() As Double
ReDim arr(count*3)
參考自:https://blog.gtwang.org/programming/excel-vba-array/ 裡面動態陣列的部分
這段可以了,請問以下應該改成.Cells(i, x).Value就可以了吧?
 .getCellByPosition(x, i).string => .Cells(i+1, x+1).Value 
Excel 好像是從 1 開始算,而 Calc 是從 0 開始算,所以才有那個 +1
例如儲存格 "B5" 在 Excel 是 Cells(5, 2)
但是在 Calc 是 getCellByPosition(1, 4)
如果不確定空值判斷的方式,建議可以把 while t <> "" 先改成  while t <> "" And i<5000 做測試,確認沒問題再拿掉 And i<5000,避免變成無窮迴圈。
跑完了...速度快的嚇人,非常感謝!!
請問這個概念是什麼呢? 通常是用在多條件比對的場合嗎?
一般搜尋資料的函式,為了通用性,並不會假設資料有排序。所以每次搜尋都是一個一個比對。這也是原本的狀況。
假設資料有排序,那麼可以用每次都對切一半,這樣找到資料就快很多。這是可以考慮優化的地方。(雖然我不是用這個)
很多程式都有類似 字典(Dictionary) 的資料結構,這種資料結構插入、搜尋的效率至少都有「對切一半」的效能(視內部實作而定),也就是  ,用 4096筆做比喻,原本要花4096單位的時間,會變成 
 單位的時間。
試試看
如果可用再來看細節
如果不能用或一樣慢就忽略這程式吧
Sub FindInRange()
    i = 1
    cate1 = "FCST"
    cate2 = "SO"
    cate3 = "Ship"
    row1 = Cells(Rows.Count, 1).End(xlUp).Row
    
    
    'Debug.Print row1
    Do Until i >= row1
        i = i + 1
        Debug.Print i
        Bnum = Range("a" & i)
        
        a = 0
        b = 0
        c = 0
        With Worksheets("aaa").Range("b:b")
            Set Rng = .Find(Bnum, LookIn:=xlValues)
            If Not Rng Is Nothing Then
                firstAddress = Rng.Address
                Do
                    sType = Rng.Offset(0, -1).Value
                    If (sType = "FCST") Then
                        a = Rng.Offset(0, 3).Value
                    ElseIf (sType = "SO") Then
                        b = Rng.Offset(0, 3).Value
                    ElseIf (sType = "Ship") Then
                        c = Rng.Offset(0, 3).Value
                    End If
                    
                    Set Rng = .FindNext(Rng)
                Loop While Not Rng Is Nothing And Rng.Address <> firstAddress
            End If
        End With
                
        
        If Cells(i, 3) = "N" Then
            If a > b + c Then
                Cells(i, 4) = b + c
            Else
                Cells(i, 4) = a
            End If
        Else
            Cells(i, 4) = a
        End If
    
    Loop
End Sub
程式可以執行,但結果都是空值(補一下頁面),然後還要再加一個限制條件該放哪呢? Bnum2是輸入值的公司,D:D是aaa中的公司行。然後FCST、SO、Ship三個值都要,找不到的話要補零因為最後還是要比較該回傳哪個值
a = Application.Evaluate("=ifna(Lookup(1,0/((aaa!A:A= """ & cate1 & """ ) * (aaa!B:B= """ & Bnum & """ )* (aaa!D:D= """ & Bnum2 & """ )), aaa!E:E),0)")


除了第二、三格正常之外,第四隔開始程式都找不到表aaa b列的值
h93243 要不要給一下測試用檔案阿?不然幫你測試還要自己建立資料。
PS. 我是幫別人說的,我手邊沒有 Excel 沒法幫忙。
程式已修改
只加了 a=0 b=0 c=0
執行結果如下
謝謝! 執行完約21分鐘(4261筆),是因為判斷的項目太多所以VBA必須要執行這麼久,還是電腦效能不夠呢? 因為目標是希望在10分鐘內完成