【问题标题】:VBA (Excel): Find Based on Multiple Search Criteria Without LoopingVBA (Excel):基于多个搜索条件的查找,无需循环
【发布时间】:2015-10-30 20:15:47
【问题描述】:

我有一个大型数据表,我想根据 3 组条件在 VBA 中进行搜索。可以假定每个行条目是唯一的。表格/数据本身的格式不能因要求而改变。 (我看过几篇关于相关问题的帖子,但还没有找到可行的解决方案。)

一开始我在循环中使用了经典的VBA find 方法:

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

但是因为这需要在大量数据上调用多次,所以速度并不好。

我做了一些搜索,发现我可以将match 方法与index 一起使用。所以我尝试了很多变体都没有成功,例如:

result = Evaluate("=MATCH(1, (""" & criteria1Name & """=A2:A" & lastRow & ")*(""" & criteria2Name & """=B2:B" & lastRow & ")*(""" & criteria3Name & """=C2:C" & lastRow & "), 0)")

result = Application.WorksheetFunction.Index(resultRange, Application.WorksheetFunction.Match((criteria1Name = criteria1Range)*(criteria2Name = criteria2Range)*(criteria3Name = criteria3Range))

result = Application.WorksheetFunction.Index(resultRange, Application.WorksheetFunction.Match((criteria1Range=criteria1Name )*(criteria2Range=criteria2Name )*(criteria3Range=criteria3Name ))

然后我尝试使用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

但是因为其中一个排序列包含日期,所以我无法让 AutoFilter 正常工作。

我的问题是,如何在 Excel VBA 中根据多个条件搜索列,不循环,返回行号或我在该行的单元格中的值有兴趣

【问题讨论】:

  • 你为什么反对循环播放?你在说多少行?
  • 你想对返回的结果做什么?

标签: excel search criteria vba


【解决方案1】:

您可以使用高级过滤器。将列标题放在工作表的单独部分(或完全不同的工作表)。在这些列标题下,将您要在每一列中查找的条件放入其中。然后将该范围(包括标题)命名为“Criteria”之类的名称。那么宏就变成了:

Sub Macro1()

    Sheets("Sheet1").Range("A1").CurrentRegion.AdvancedFilter xlFilterInPlace, Range("Criteria")

End Sub

作为我下面评论的后续,让 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

【讨论】:

  • 这是一个有趣的方法。但是,我无法以任何方式修改工作簿的内容,并且该组标准是基于另一组输入的动态的。我真的在寻找一种专门在 VBA 中执行此操作的方法。
  • 您可以在 vba 中创建条件范围,甚至可以创建一个临时工作表,之后将被删除,只需禁用屏幕更新,用户将永远看不到幕后的东西
  • 我已经更新了答案,以扩展我在之前评论中所说的内容
  • +1 更简单一点是构建一个 IF 公式测试所有三个标准,然后在单个列中过滤 True 值
  • +1。我已经成功过滤了多个条件。在我对过滤后的值做了我需要做的事情之后,我希望能够取消过滤电子表格;但是,不显示标题中的过滤箭头。是否可以让它与过滤一起出现?我解决了这个问题,实现了另一个带有空搜索条件的宏来过滤数据中的所有行。
【解决方案2】:

您可以将EVALUATE 用于多个条件,例如返回数学值的行号。这使用与Is it possible to fill an array with row numbers which match a certain criteria without looping?相同的方法

  • 它搜索 50000 行来匹配
    • A 列中的前四个字母与fred 匹配
    • B 中的日期大于1/1/2001
    • apple 在第 5 列中
  • 任何符合这些条件的行都会重新调整为x 中的行号

(下图中第 1 行和第 5 行)

代码

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 限制为 65536 个单元格,因此需要将更长的范围“分块”成碎片。

【讨论】:

  • 您忘记在代码中添加插入Dim x了吗?将行号存储在 x 中后,您将如何过滤这些行?谢了。
  • 不,X 是一个变种。
  • 所以是的,他忘了添加Dim x as Variant。始终使用Dim,即使是变体。
  • 但更好的是Dim x() As String,因为它返回的是:字符串数组。不错的单线,+1。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-02-08
  • 1970-01-01
  • 2021-11-04
相关资源
最近更新 更多