Excel 合并 Vlookup
Excel combine Vlookups
我有两个文件,一个是包含项目关键信息的项目登记册,另一个是风险日志。
注册表中的条目与风险日志之间存在 1:m 关系。我需要做的是将所有项目风险合并到项目注册文件中的一个单元格中。
两个文件中的匹配字段是项目 ID 字段
有没有办法使用 vlookup 变体或多个嵌套 vlookup 来做到这一点?
重新编辑:
您可能需要编写一个 user defined function 或编写一个宏(代码相同 link)
这是我提到的用户定义函数方法(改编自我已经制作的不同 VLOOKUP 变体):
' Acts like VLOOKUP in a 1-to-many scenario by concatenating all values in matching rows
' instead of just returning the first match
Public Function VLOOKUP_MANY(lookup_value As String, lookup_range As Range, column_number As Integer, Optional delimiter As Variant) As Variant
Dim vArr As Variant
Dim i As Long
Dim found As Boolean: found = False
' Set default delimiter
If IsMissing(delimiter) Then delimiter = ", "
' Get values
vArr = lookup_range.Value2
' If column_number is outside of the specified range, return #REF
If column_number < LBound(vArr, 2) Or column_number > UBound(vArr, 2) Then
VLOOKUP_MANY = CVErr(xlErrRef)
Exit Function
End If
' Search for matches and build a concatenated list
VLOOKUP_MANY = ""
For i = 1 To UBound(vArr, 1)
If UCase(vArr(i, 1)) = UCase(lookup_value) Then
VLOOKUP_MANY = VLOOKUP_MANY & delimiter & vArr(i, column_number)
found = True ' Mark at least 1 result
End If
Next
If found Then
VLOOKUP_MANY = Right(VLOOKUP_MANY, Len(VLOOKUP_MANY) - Len(delimiter)) ' Remove first delimiter
Else
VLOOKUP_MANY = CVErr(xlErrNA) ' If no matches found, return #N/A
End If
End Function
这将在指定范围的第一列中搜索指定值(与 VLOOKUP 相同),但 return 将指定列编号中的值连接起来。如果未找到匹配项,它将 return #N/A ,如果为列号指定了无效值(例如,您选择第 5 列但只有 4 列 table ).
如果您不了解用户定义的函数 - 您可以将此 VBA 代码复制到工作簿中某个模块的 VBE 中。按 Alt+F11,转到屏幕顶部的 Insert > Module
,然后将此代码粘贴到打开的空白文件中。保存时,您必须将工作簿保存为启用宏 (.xlsm) 以保持代码正常工作 - Excel 将在保存屏幕中提醒您。
预先警告:它会比 VLOOKUP 慢,因为它必须查看整个查找范围,而不是能够在找到的第一个匹配项处停止。
如果您愿意改用数组公式,可以通过多种方法为非常大的数据集加速此类功能...
利用数组公式的一些优势来存储查找值并加速后续调用的不同版本:
' Acts like VLOOKUP in a 1-to-many scenario by concatenating all values in matching rows
' instead of just returning the first match
' Utilizes a dictionary to speedup multiple matches (great for array formulas)
Public Function VLOOKUP_MANY_ARRAY(lookup_values As Range, lookup_range As Range, column_number As Integer, Optional delimiter As Variant) As Variant
Dim vHaystack As Variant, vNeedles As Variant
Dim i As Long
Dim found As Boolean: found = False
Dim dict As Object: Set dict = CreateObject("Scripting.Dictionary")
' Set default delimiter
If IsMissing(delimiter) Then delimiter = ", "
' Get values
vHaystack = lookup_range
vNeedles = lookup_values
' If column_number is outside of the specified range, return #REF
If column_number < LBound(vHaystack, 2) Or column_number > UBound(vHaystack, 2) Then
VLOOKUP_MANY_ARRAY = CVErr(xlErrRef)
Exit Function
End If
' Add values to a lookup dictionary
For i = 1 To UBound(vHaystack, 1)
If dict.Exists(UCase(vHaystack(i, 1))) Then
dict.Item(UCase(vHaystack(i, 1))) = dict.Item(UCase(vHaystack(i, 1))) & delimiter & vHaystack(i, column_number)
Else
dict.Add UCase(vHaystack(i, 1)), vHaystack(i, column_number)
End If
Next
Dim outArr As Variant
If IsArray(vNeedles) Then ' Check number of lookup cells
' Build output array
ReDim outArr(1 To UBound(vNeedles, 1), 1 To 1) As Variant
For i = 1 To UBound(vNeedles, 1)
If dict.Exists(UCase(vNeedles(i, 1))) Then
outArr(i, 1) = dict.Item(UCase(vNeedles(i, 1)))
Else
outArr(i, 1) = CVErr(xlErrNA)
End If
Next
Else
' Single output value
If dict.Exists(UCase(vNeedles)) Then
outArr = dict.Item(UCase(vNeedles))
Else
outArr = CVErr(xlErrNA)
End If
End If
VLOOKUP_MANY_ARRAY = outArr
End Function
这将创建一个 Dictionary
,这是一个非常适合查找值的特殊结构。构建它会涉及一些额外的开销,但是一旦有了结构,就可以非常快速地对其进行查找。这对于数组公式特别好,基本上是当 完全相同的 相同的公式被放入整个单元格集合时,然后函数执行一次并且每个 returns 值单元格(而不是只对一堆单元格单独执行一次)。使用 CTRL+SHIFT+ENTER 像数组公式一样输入它,并使第一个参数引用 所有 您的查找值而不是一个。
它可以在不用作数组公式的情况下工作,但在那种情况下它会比第一个函数慢一些。但是,如果您在数组公式中使用它,您会看到 巨大 加速。
我有两个文件,一个是包含项目关键信息的项目登记册,另一个是风险日志。
注册表中的条目与风险日志之间存在 1:m 关系。我需要做的是将所有项目风险合并到项目注册文件中的一个单元格中。
两个文件中的匹配字段是项目 ID 字段
有没有办法使用 vlookup 变体或多个嵌套 vlookup 来做到这一点?
重新编辑:
您可能需要编写一个 user defined function 或编写一个宏(代码相同 link)
这是我提到的用户定义函数方法(改编自我已经制作的不同 VLOOKUP 变体):
' Acts like VLOOKUP in a 1-to-many scenario by concatenating all values in matching rows
' instead of just returning the first match
Public Function VLOOKUP_MANY(lookup_value As String, lookup_range As Range, column_number As Integer, Optional delimiter As Variant) As Variant
Dim vArr As Variant
Dim i As Long
Dim found As Boolean: found = False
' Set default delimiter
If IsMissing(delimiter) Then delimiter = ", "
' Get values
vArr = lookup_range.Value2
' If column_number is outside of the specified range, return #REF
If column_number < LBound(vArr, 2) Or column_number > UBound(vArr, 2) Then
VLOOKUP_MANY = CVErr(xlErrRef)
Exit Function
End If
' Search for matches and build a concatenated list
VLOOKUP_MANY = ""
For i = 1 To UBound(vArr, 1)
If UCase(vArr(i, 1)) = UCase(lookup_value) Then
VLOOKUP_MANY = VLOOKUP_MANY & delimiter & vArr(i, column_number)
found = True ' Mark at least 1 result
End If
Next
If found Then
VLOOKUP_MANY = Right(VLOOKUP_MANY, Len(VLOOKUP_MANY) - Len(delimiter)) ' Remove first delimiter
Else
VLOOKUP_MANY = CVErr(xlErrNA) ' If no matches found, return #N/A
End If
End Function
这将在指定范围的第一列中搜索指定值(与 VLOOKUP 相同),但 return 将指定列编号中的值连接起来。如果未找到匹配项,它将 return #N/A ,如果为列号指定了无效值(例如,您选择第 5 列但只有 4 列 table ).
如果您不了解用户定义的函数 - 您可以将此 VBA 代码复制到工作簿中某个模块的 VBE 中。按 Alt+F11,转到屏幕顶部的 Insert > Module
,然后将此代码粘贴到打开的空白文件中。保存时,您必须将工作簿保存为启用宏 (.xlsm) 以保持代码正常工作 - Excel 将在保存屏幕中提醒您。
预先警告:它会比 VLOOKUP 慢,因为它必须查看整个查找范围,而不是能够在找到的第一个匹配项处停止。
如果您愿意改用数组公式,可以通过多种方法为非常大的数据集加速此类功能...
利用数组公式的一些优势来存储查找值并加速后续调用的不同版本:
' Acts like VLOOKUP in a 1-to-many scenario by concatenating all values in matching rows
' instead of just returning the first match
' Utilizes a dictionary to speedup multiple matches (great for array formulas)
Public Function VLOOKUP_MANY_ARRAY(lookup_values As Range, lookup_range As Range, column_number As Integer, Optional delimiter As Variant) As Variant
Dim vHaystack As Variant, vNeedles As Variant
Dim i As Long
Dim found As Boolean: found = False
Dim dict As Object: Set dict = CreateObject("Scripting.Dictionary")
' Set default delimiter
If IsMissing(delimiter) Then delimiter = ", "
' Get values
vHaystack = lookup_range
vNeedles = lookup_values
' If column_number is outside of the specified range, return #REF
If column_number < LBound(vHaystack, 2) Or column_number > UBound(vHaystack, 2) Then
VLOOKUP_MANY_ARRAY = CVErr(xlErrRef)
Exit Function
End If
' Add values to a lookup dictionary
For i = 1 To UBound(vHaystack, 1)
If dict.Exists(UCase(vHaystack(i, 1))) Then
dict.Item(UCase(vHaystack(i, 1))) = dict.Item(UCase(vHaystack(i, 1))) & delimiter & vHaystack(i, column_number)
Else
dict.Add UCase(vHaystack(i, 1)), vHaystack(i, column_number)
End If
Next
Dim outArr As Variant
If IsArray(vNeedles) Then ' Check number of lookup cells
' Build output array
ReDim outArr(1 To UBound(vNeedles, 1), 1 To 1) As Variant
For i = 1 To UBound(vNeedles, 1)
If dict.Exists(UCase(vNeedles(i, 1))) Then
outArr(i, 1) = dict.Item(UCase(vNeedles(i, 1)))
Else
outArr(i, 1) = CVErr(xlErrNA)
End If
Next
Else
' Single output value
If dict.Exists(UCase(vNeedles)) Then
outArr = dict.Item(UCase(vNeedles))
Else
outArr = CVErr(xlErrNA)
End If
End If
VLOOKUP_MANY_ARRAY = outArr
End Function
这将创建一个 Dictionary
,这是一个非常适合查找值的特殊结构。构建它会涉及一些额外的开销,但是一旦有了结构,就可以非常快速地对其进行查找。这对于数组公式特别好,基本上是当 完全相同的 相同的公式被放入整个单元格集合时,然后函数执行一次并且每个 returns 值单元格(而不是只对一堆单元格单独执行一次)。使用 CTRL+SHIFT+ENTER 像数组公式一样输入它,并使第一个参数引用 所有 您的查找值而不是一个。
它可以在不用作数组公式的情况下工作,但在那种情况下它会比第一个函数慢一些。但是,如果您在数组公式中使用它,您会看到 巨大 加速。