I have a large data sheet that I want to search in VBA based on 3 sets of criteria. Each row entry can be assumed to be unique. The format of the sheet/data itself cannot be changed due to requirements. (I've seen several posts on related questions but haven't found a working solution for this yet.)
我有一个大型数据表,我想根据3套标准在VBA中搜索。可以假设每个行条目是唯一的。由于要求,无法更改工作表/数据本身的格式。 (我在相关问题上看过几篇帖子,但还没有找到一个有效的解决方案。)
At first I used the classic VBA find method in a loop:
起初我在循环中使用了经典的VBA查找方法:
Set foundItem = itemRange.Find(What:=itemName, Lookin:=xlValues, lookat:=xlWhole, SearchOrder:=xlByRows)
If Not foundItem Is Nothing Then
firstMatchAddr = foundItem.Address
Do
' *Check the other fields in this row for a match and exit if found*
Set foundItem = itemRange.FindNext(foundItem)
Loop While foundItem.Address <> firstMatchAddr And Not foundItem Is Nothing
End If
But because this needs to be called a number of times on large sets of data, the speed of this was no good.
但是因为在大型数据集上需要多次调用,所以速度并不好。
I did some searching and found that I could use the match method with index. So I unsuccessfully tried many variations of that such as:
我做了一些搜索,发现我可以使用带索引的匹配方法。所以我没有尝试过很多变化,例如:
result = Evaluate("=MATCH(1, (""" & criteria1Name & """=A2:A" & lastRow & ")*(""" & criteria2Name & """=B2:B" & lastRow & ")*(""" & criteria3Name & """=C2:C" & lastRow & "), 0)")
And
和
result = Application.WorksheetFunction.Index(resultRange, Application.WorksheetFunction.Match((criteria1Name = criteria1Range)*(criteria2Name = criteria2Range)*(criteria3Name = criteria3Range))
And
和
result = Application.WorksheetFunction.Index(resultRange, Application.WorksheetFunction.Match((criteria1Range=criteria1Name )*(criteria2Range=criteria2Name )*(criteria3Range=criteria3Name ))
Then I tried using AutoFilter to sort:
然后我尝试使用AutoFilter进行排序:
.Range(.Cells(1,1), .Cells(lastRow, lastCol)).AutoFilter Field:=1, Criteria1:="=" & criteria1Name
.Range(.Cells(1,1), .Cells(lastRow, lastCol)).AutoFilter Field:=2, Criteria1:="=" & criteria2Name
.Range(.Cells(1,1), .Cells(lastRow, lastCol)).AutoFilter Field:=3, Criteria1:="=" & criteria3Name
But because one of the sorting columns contains dates, I had issues getting AutoFilter to work properly.
但由于其中一个排序列包含日期,因此我遇到了使AutoFilter正常工作的问题。
My question is, how can I search through columns in Excel VBA based on multiple criteria, without looping, returning either the row number or the value in the cell of that row that I am interested in?
我的问题是,我如何根据多个条件搜索Excel VBA中的列,而不循环,返回行号或我感兴趣的那行的单元格中的值?
2 个解决方案
#1
6
You could use an Advanced Filter. Put the column headers in a separate part of the sheet (or a different sheet altogether). Under those column headers, put the criteria you're looking for in each column. Then name that range (including the headers) something like "Criteria". Then the macro becomes:
您可以使用高级过滤器。将列标题放在工作表的单独部分(或完全不同的工作表)。在这些列标题下,将您要查找的条件放在每列中。然后将该范围(包括标题)命名为“Criteria”。然后宏变成:
Sub Macro1()
Sheets("Sheet1").Range("A1").CurrentRegion.AdvancedFilter xlFilterInPlace, Range("Criteria")
End Sub
As a follow up to my comment below, to have the VBA create the criteria range behind the scenes:
作为我在下面的评论的后续跟进,让VBA在幕后创建标准范围:
Sub Macro1()
'Code up here that defines the criteria
Application.ScreenUpdating = False
Application.DisplayAlerts = False
With Sheets.Add
'Create the advanced filter criteria range
.Range("A1") = "HeaderA"
.Range("B1") = "HeaderB"
.Range("C1") = "HeaderC"
.Range("A2") = criteria1Name
.Range("B2") = criteria2Name
.Range("C2") = criteria3Name
'Alternately, to save space:
'.Range("A1:C1").Value = Array("HeaderA", "HeaderB", "HeaderC")
'.Range("A2:C2").Value = Array(criteria1Name, criteria2Name, criteria3Name)
'Then perform the advanced filter
Sheets("Sheet1").Range("A1").CurrentRegion.AdvancedFilter xlFilterInPlace, .Range("A1:C2")
'Remove behind the scenes sheet now that the filter is completed
.Delete
End With
Application.ScreenUpdating = True
Application.DisplayAlerts = True
End Sub
#2
3
You can use EVALUATE
for multiple criteria like so to return the row numbers of mathcing values. This uses the same approach as Is it possible to fill an array with row numbers which match a certain criteria without looping?
您可以将EVALUATE用于多个条件,如此返回数学值的行数。这使用相同的方法,因为可以使用符合特定条件的行号填充数组而不循环?
- It searches 50000 rows to match
- the first four letters in column A matches
fred
- A列中的前四个字母与fred匹配
- the date in B to be greater than
1/1/2001
- B中的日期大于1/1/2001
-
apple
in column 5 - 第5栏中的苹果
- the first four letters in column A matches
- 它搜索50000行以匹配列A中的前四个字母匹配fred B中的日期大于第5列中的1/1/2001 apple
- Any rows matching these criteria are retuned as row numbers in
x
- 符合这些条件的任何行都将作为x中的行号重新调整
(rows 1 and 5 in picture below)
(下图中的第1行和第5行)
code
码
Sub GetEm2()
x = Filter(Application.Transpose(Application.Evaluate("=IF((LEFT(A1:A10000,4)=""fred"")*(B1:B10000>date(2001,1,1))*(C1:C10000=""apple""),ROW(A1:A10000),""x"")")), "x", False)
End Sub
Application.Transpose
is limited to 65536 cells, so a longer range needs to be "chunked" into pieces.
Application.Transpose限制为65536个单元格,因此较长的范围需要“分块”成碎片。
#1
6
You could use an Advanced Filter. Put the column headers in a separate part of the sheet (or a different sheet altogether). Under those column headers, put the criteria you're looking for in each column. Then name that range (including the headers) something like "Criteria". Then the macro becomes:
您可以使用高级过滤器。将列标题放在工作表的单独部分(或完全不同的工作表)。在这些列标题下,将您要查找的条件放在每列中。然后将该范围(包括标题)命名为“Criteria”。然后宏变成:
Sub Macro1()
Sheets("Sheet1").Range("A1").CurrentRegion.AdvancedFilter xlFilterInPlace, Range("Criteria")
End Sub
As a follow up to my comment below, to have the VBA create the criteria range behind the scenes:
作为我在下面的评论的后续跟进,让VBA在幕后创建标准范围:
Sub Macro1()
'Code up here that defines the criteria
Application.ScreenUpdating = False
Application.DisplayAlerts = False
With Sheets.Add
'Create the advanced filter criteria range
.Range("A1") = "HeaderA"
.Range("B1") = "HeaderB"
.Range("C1") = "HeaderC"
.Range("A2") = criteria1Name
.Range("B2") = criteria2Name
.Range("C2") = criteria3Name
'Alternately, to save space:
'.Range("A1:C1").Value = Array("HeaderA", "HeaderB", "HeaderC")
'.Range("A2:C2").Value = Array(criteria1Name, criteria2Name, criteria3Name)
'Then perform the advanced filter
Sheets("Sheet1").Range("A1").CurrentRegion.AdvancedFilter xlFilterInPlace, .Range("A1:C2")
'Remove behind the scenes sheet now that the filter is completed
.Delete
End With
Application.ScreenUpdating = True
Application.DisplayAlerts = True
End Sub
#2
3
You can use EVALUATE
for multiple criteria like so to return the row numbers of mathcing values. This uses the same approach as Is it possible to fill an array with row numbers which match a certain criteria without looping?
您可以将EVALUATE用于多个条件,如此返回数学值的行数。这使用相同的方法,因为可以使用符合特定条件的行号填充数组而不循环?
- It searches 50000 rows to match
- the first four letters in column A matches
fred
- A列中的前四个字母与fred匹配
- the date in B to be greater than
1/1/2001
- B中的日期大于1/1/2001
-
apple
in column 5 - 第5栏中的苹果
- the first four letters in column A matches
- 它搜索50000行以匹配列A中的前四个字母匹配fred B中的日期大于第5列中的1/1/2001 apple
- Any rows matching these criteria are retuned as row numbers in
x
- 符合这些条件的任何行都将作为x中的行号重新调整
(rows 1 and 5 in picture below)
(下图中的第1行和第5行)
code
码
Sub GetEm2()
x = Filter(Application.Transpose(Application.Evaluate("=IF((LEFT(A1:A10000,4)=""fred"")*(B1:B10000>date(2001,1,1))*(C1:C10000=""apple""),ROW(A1:A10000),""x"")")), "x", False)
End Sub
Application.Transpose
is limited to 65536 cells, so a longer range needs to be "chunked" into pieces.
Application.Transpose限制为65536个单元格,因此较长的范围需要“分块”成碎片。