Excel 公式或 VBA:使用 2 列条件在单独的 table 中查找匹配地址 - 无辅助列

Excel Formula or VBA: Find match's address in separate table with 2 column criteria - No helper columns

我需要公式结构方面的帮助,

我有 2 个 table。我想找到 a 列和 b 列相等的匹配项,并在 table 2 中获取地址。它们将是唯一的条目。例如:

项目信息表:

         A      |       B      |     C     |
  ------------------------------------------
1 |     Name    |    Company   |  Project  |
  ------------------------------------------
2 | Chris Evans |     Apple    |   Info    |
  ------------------------------------------
3 | Chris Evans |    Google    |   Info    |
  ------------------------------------------
4 | Bill Gates  |  Burger King |   Info    |
  ------------------------------------------

客户信息表:

         A      |       B      |   C   |   D
  -------------------------------------------
1 |    Client   |    Company   |  Age  |  Sex | 
  -------------------------------------------
2 | Chris Evans |     Apple    |   12  |   M  |
  -------------------------------------------
3 | Chris Evans |    Google    |   17  |   M  |
  -------------------------------------------
4 | Bill Gates  |  Burger King |   98  |   F  |
  -------------------------------------------

我希望能够在 'ProjectInfoTable' 中获取 'ClientInfoTable'[=16 中匹配的姓名和公司客户的地址=]

我遇到的麻烦 运行 可能有上千个不同的 Chris Evans,所以 VLOOKUP 在这里不好用。我需要通过交叉引用他们的公司

来确保 'ClientInfoTable' 中的 Chris Evans 与我在 'ProjectInfoTable' 中看到的是同一个人

如果我只按名字搜索地址就没问题:

=ADDRESS(ROW(INDEX(ClientInfoTable,MATCH([@[Client]],ClientInfoTable[Client],0),1)),COLUMN(INDEX(ClientInfoTable,MATCH([@[Client]],ClientInfoTable[Client],0),1)),1,1,"Clients")

但我需要添加他们公司的附加条件,所以现在该公式已无用。

有什么想法吗?我不想使用隐藏列,或者 'helper columns'

我会排除 VBA 或基于公式的答案。我什至会奖励任何能同时提供两者的人,假设数据将始终是动态范围, 然后会很好地解释你的 code/formula。我是来学习的,我不是 copy/paste 类型的用户,解释很长。

你可以使用 AutoFilter():

Option Explicit

Sub main()
    Dim rng As Range

    With ActiveSheet.ListObjects("ClientInfoTable").Range '<--| reference 'ClientInfoTable' range
        .AutoFilter field:=1, Criteria1:="Chris Evans" '<--| filter it on its 1st column with "Chris Evans"
        .AutoFilter field:=2, Criteria1:="Google" '<--| filter it on its 2nd column with "Google"
        If Application.WorksheetFunction.Subtotal(103, .Resize(, 1)) > 1 Then
            Set rng = .Resize(.Rows.Count - 1).Offset(1).SpecialCells(xlCellTypeVisible).Cells(1, 1) '<--| if any filtered cells other than header then set the range corresponding to upleftmost filtered cell below the headers row
            MsgBox "Found at " & rng.Address
        End If
        .AutoFilter '<--| show rows back visible
    End With
End Sub

解决您的问题的一种方法是通过 ADODB 使用 Excel VBA 中的 SQL 支持。此 Microsoft article 解释了如何执行此操作。

使用 SQL 支持,您基本上有两种选择:要么在 VBA 中编写一个函数,该函数 returns 来自给定名称和公司的(第一个)值ClientInfoTable,使用 SELECT 语句,或者您在 VBA 中编写一个子程序,直接在 ProjectInfoTable 中任何需要的地方插入所有行的值,使用ProjectInfoTable 以及名称和公司上的 ClientInfoTable

您还可以将函数 I wrote.Put 用于您要使用的文件中的任何模块:

Option Compare Text
Option Explicit
Public Function doubleMatch(criteria1 As Range, criteria1_Range As Range, criteria2 As Range, criteria2_Range As Range) As Long
Dim crit1 As Variant, crit2 As Variant
Dim crit1_ARR(), crit2_ARR() 'Arrays where criteria ranges are stored
Dim i As Long, j As Long
Dim u1 As Long, l1 As Long

crit1_ARR = criteria1_Range.Value2
crit2_ARR = criteria2_Range.Value2
crit1 = criteria1.Value
crit2 = criteria2.Value

doubleMatch = -1
'checking if ranges have the same height
If (UBound(crit1_ARR) <> UBound(crit2_ARR)) Then
    GoTo endFunc
End If

'checking if ranges are one col wide
If (LBound(crit1_ARR, 2) <> UBound(crit1_ARR, 2)) Then
    GoTo endFunc
End If

If (LBound(crit2_ARR, 2) <> UBound(crit2_ARR, 2)) Then
    GoTo endFunc
End If

l1 = LBound(crit1_ARR)
u1 = UBound(crit1_ARR)

For i = l1 To u1
    If (crit1 = crit1_ARR(i, 1)) Then
        If (crit2 = crit2_ARR(i, 1)) Then
            doubleMatch = i + Abs(l1 = 0) * 1
            GoTo endFunc
        End If
    End If
Next i

endFunc:

End Function

使用示例:

万一出错就returns -1;否则它 returns 它匹配的行数。

如果您想要完整地址,可以将此函数与 ADDRESS 和 MATCH 函数一起使用:

=ADDRESS(doubleMatch(H2;A:A;I2;B:B);MATCH("Client";A:A;0))

您可以结合使用 VLookUp 和 Choose 来获得多个条件。在你的例子中:

=VLookUp(A1&","&B1, Choose({1.2}, ClientInfoTable!A1:A4&","&ClientInfoTable!B1:B4,ClientInfoTable!C1:C4), 2, False)

这是一个矩阵公式所以你必须用Ctrl+Shift+Enter[=30关闭它=].

发生的情况如下:选择函数选择给定的第一个和第二个值(这就是第一个参数 {1.2} 所说的)。由于这是一个矩阵公式,您不仅可以获得一对值,而且每一行都有一个值。在这个例子中你得到一个矩阵:

A-----------------------|B----------
Chris Evans, Apple      |12
Chris Evans, Google     |17
Bill Gates, Burger King |98

这是您现在可以用于 VLookUp 的矩阵。

您应该确保分隔符(本例中为逗号)不会出现在您的数据中。

如果您只需要地址而不需要实际值,请使用函数 Match 而不是 vlookup:

=Match(A1&","B1, Choose({1.2}, ClientInfoTable!A1:A4&","&B1:B4,ClientInfoTable!C1:C4))

此 returns 相应集合在给定矩阵中的行。如果矩阵不是从第 1 行开始,则必须添加矩阵位置。

公式:

这是一个只有公式的解决方案,没有隐藏/辅助列,也没有数组公式:

=ADDRESS(
    ROW(
        ClientInfo
    ) - 1 +
    MATCH(
        1,
        INDEX(
            --INDEX(
                ClientInfo[Client] = $A5,
                0
            ) *
            --INDEX(
                ClientInfo[Company] = $B5,
                0
            ),
            0
        ),
        0
    ),
    COLUMN(ClientInfo)
)

组件:

a --INDEX(ClientInfo[Client]=$A5,0) - returns 一个布尔数组,用于匹配的数量,例如Chris EvansClientInfo[Client] 中。在下面的示例中,这将是 {TRUE,TRUE,FALSE,FALSE}。然后使用双一元运算符将其转换为整数数组以离开 {1,1,0,0}

b --INDEX(ClientInfo[Company]=$B5,0) - 与 a 相同,例如示例中 ClientInfo[Company] 中的 Apple 是数组 {TRUE,FALSE,FALSE,TRUE} - 然后将其转换为 {1,0,0,1}

c INDEX(a*b,0) - 将数组 a 的元素 1..n 与数组 [ 的元素 1..n 相乘=66=]b。在我们的示例中,这会导致 {1,0,0,0} ,此时您已将 Chris Evans 和 Apple 的匹配组合键标识为 ClientInfo

的第一行

d MATCH(1,c,0) - 获取数组中 1 的索引,在我们的 Chris Evans 和 Apple 示例中为 1。您提到了 它们将是独一无二的条目,所以我认为我们在这里没有问题。

e ROW(ClientInfo)-1+d - 我将 ClientInfo 定义为范围为 A8:D12 的 Table/ ListObject 但引用正在回馈 A9:D12,这似乎是 Tables/ ListObjects 的命名范围的工作方式。所以我们需要从该范围的 ROW 中减去一个以获得偏移量的开始;然后简单地添加 d.

的结果

f ADDRESS(e,COLUMN(ClientInfo)) - returns e的单元格地址和[=26的第一列=] table.

示例:

VBA:

使用上面的示例,VBA 方法将执行以下操作:

  1. 假设未找到匹配项
  2. 迭代 table
  3. 的行
  4. 获取候选值并检查引用列的输入
  5. 如果两者匹配则退出返回地址的循环

代码:

Option Explicit

Sub Test()

    MsgBox GetAddressOfKey("Client", "Chris Evans", "Company", "Apple", "ClientInfo")
    MsgBox GetAddressOfKey("Client", "Chris Evans", "Company", "Google", "ClientInfo")
    MsgBox GetAddressOfKey("Client", "Bill Gates", "Company", "Burger King", "ClientInfo")

End Sub

Function GetAddressOfKey(col1 As String, val1 As String, col2 As String, val2 As String, strTable As String) As String

    Dim lst As ListObject
    Dim lr As ListRow
    Dim strAddress As String
    Dim strCandidate1 As String
    Dim strCandidate2 As String

    strAddress = ""
    Set lst = ActiveSheet.ListObjects(strTable)

    'iterate rows
    For Each lr In lst.ListRows
        'get candidate values
        strCandidate1 = Intersect(lr.Range, lst.ListColumns(col1).Range).Value
        strCandidate2 = Intersect(lr.Range, lst.ListColumns(col2).Range).Value
        'check against inputs
        If strCandidate1 = val1 And strCandidate2 = val2 Then
            strAddress = lst.Range.Cells(lr.Index + 1, 1).Address
            'quit if we find a match
            Exit For
        End If
    Next lr

    'return
    GetAddressOfKey = strAddress

End Function

PS 我犹豫要不要提供 VBA 答案,而你已经接受了一个不错的答案。但是,我稍微倾向于在不更新 UI 的情况下执行此操作,尽管我同意 AutoFilter 方法已经足够好了。 HTH

创建一个 VBA 函数,将客户名称和公司作为字符串参数 - 然后遍历客户 table 和 return 地址(如果找到的话)。如果没有匹配项,下面的代码将 return "Not found#"

Option Explicit
Option Compare Text
Public Function SearchAddress(clientName As String, company As String) As String
    Dim x As Integer
    Dim shtClients As Worksheet
    Set shtClients = Sheets("Clients") 'Clients = name of the sheet with ClientTableInfo
    x = 2

    'loop through the clients table searching for the row where the clientname and companyname is what is supplied to the function

    Do
        'Column A has ClientName of ClientTableInfo
        'Column B has companyName of ClientTableInfo
        '.............
        'Column E has Address of ClientTableInfo <-- our search target
        If shtClients.Range("A" & x).Value = clientName And shtClients.Range("B" & x).Value = company Then
            SearchAddress = shtClients.Range("E" & x).Value 'column E has the address in the clients info table
            Exit Function
        End If
        x = x + 1
    Loop Until x > shtClients.UsedRange.Rows.Count
    SearchAddress = "Not found#"
End Function

要使用此功能,请将代码作为 VBA 模块导入宏工作簿(记得启用宏)。然后像普通 excel 公式一样将公式键入单元格

=SearchAddress(ProjectInfoTable[@Name],ProjectInfoTable[@Company])

=SearchAddress(A1,A2)