2016-08-12 68 views
1

我有两个文件,一个是项目注册表,它包含关于项目的关键信息,另一个是风险日志。Excel合并Vlookups

注册表和风险日志中的条目之间存在1:m的关系。我需要做的是将所有项目风险合并到项目寄存器文件中的一个单元格中。

匹配场这两个文件是项目ID字段

有没有一种方法,我可以做到这一点使用VLOOKUP变体或多重嵌套vlookups?

+0

我这么认为,但可能并不美观。'VLOOKUP'只提供一个值,如果你知道风险的最高可能值'm',则可以使用'm'嵌套'VLOOKUP'。 –

+0

这与VLOOKUP完成有多重要?如果愿意,VBA用户定义的函数可以完成所有这些。 – Mikegrann

回答

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;如果为列号指定了无效值(例如,选择第5列,但只有4列表),则返回#REF。

如果您不知道用户定义的函数 - 您可以将此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,这是一种特殊的结构,这对查找值确实不错。构建它需要额外的一些额外开销,但是一旦你拥有了结构,你就可以很快地进行查找。这对于数组公式尤其好,基本上当同一个公式被放入一个完整的单元集合中时,那么该函数执行一次并为每个单元返回值(而不是仅针对一堆执行一次的细胞)。像使用CTRL + SHIFT + ENTER的数组公式一样输入它,并使第一个参数指向所有您的查找值而不是一个。

它可以在不用作数组公式的情况下工作,但它会比那种情况下的第一个函数慢一些。但是,如果你在一个数组公式中使用它,你会看到巨大的加速。

+0

谢谢 - 这是完美的 – Quinn

+0

我在尝试使用数组公式时遇到问题,错误是参数在VLOOKUP_MANY = CVErr(xlErrRef)代码行中不可选。任何想法可能导致这一点我很确定我输入了所有需要的变量。 – Quinn

+0

@奎因是的,那一行是错误的。我忘记更改变量名称,同时重构用于数组公式中的代码。它应该读取'VLOOKUP_MANY_ARRAY = CVErr(xlErrRef)'。我不确定这是否正是您遇到的问题(这不是我期望的错误代码),但是这绝对是一个现在已经解决的问题。 – Mikegrann

0

重新编辑:

您可能需要编写一个user defined function或写一个宏(在相同的链接代码)

+0

对不起,我应该更清楚地了解风险记录风险记录在不同的线路上,因此一个项目可能在不同的线路上有5个以上的风险。我不认为将Vlookups结合起来可以处理我目前的数据。如果我错了,请纠正我的错误 – Quinn

+0

@Quinn您指定了1:m的关系,所以我认为这已经很清楚了。 – Mikegrann

+0

我编辑了我的答案,希望它适用于您 –