我有两个文件,一个是项目注册,它包含项目的关键信息,另一个是风险日志。
注册表和风险日志中的条目之间存在1:m的关系。我需要做的是将所有项目风险合并到项目寄存器文件中的一个单元格中。
两个文件中的匹配字段是项目ID字段
有没有办法可以使用vlookup变体或多个嵌套的vlookup来做到这一点?
答案 0 :(得分:2)
这里是我提到的用户定义的函数方法(改编自我已经制作的不同的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相同),但返回连接的指定列号中的值。如果没有找到匹配项,它将返回#N / A;如果为列号指定了无效值,则返回#REF(例如,您选择第5列但只有4列表)。
如果您不了解用户定义的函数 - 您只需将此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
,这是一个特殊的结构,非常适合查找值。构建它需要一些额外的开销,但是一旦你有了这个结构,你就可以很快地进行查找。这对于数组公式来说尤其不错,基本上当 exact 相同的公式被放入整个单元格集合中时,该函数执行一次并返回每个单元格的值(而不是仅执行一次,单独地,对于一堆细胞)。使用CTRL + SHIFT + ENTER将其输入为数组公式,并使第一个参数引用 all 您的查找值,而不只是一个。
它可以在不用作数组公式的情况下工作,但它会比那种情况下的第一个函数慢一些。但是,如果您在数组公式中使用它,您将看到巨大的加速。
答案 1 :(得分:0)
重新编辑:
您可能需要编写user defined function或编写宏(代码在同一链接上)