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 Evans
在 ClientInfo[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 方法将执行以下操作:
- 假设未找到匹配项
- 迭代 table
的行
- 获取候选值并检查引用列的输入
- 如果两者匹配则退出返回地址的循环
代码:
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)
我需要公式结构方面的帮助,
我有 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 Evans
在 ClientInfo[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 方法将执行以下操作:
- 假设未找到匹配项
- 迭代 table 的行
- 获取候选值并检查引用列的输入
- 如果两者匹配则退出返回地址的循环
代码:
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)