2016-08-12 4 views
1

У меня есть два файла, один из которых представляет собой реестр проектов, содержащий ключевую информацию о проекте, а другой - журнал рисков.Excel комбинация Vlookups

Существует связь 1: m между записями в Регистре и журналом рисков. Что мне нужно сделать, это объединить все риски проекта в одну ячейку внутри файла регистра проекта.

Поле соответствия в обоих файлах это поле Project ID

Есть ли способ, что я могу сделать это, используя вариант ВПР или несколько вложенных vlookups?

+0

Я так думаю, но это, вероятно, не очень.'VLOOKUP' предоставляет только одно значение, бит, если вы знаете, как максимально возможный« м »для риска, вы можете использовать' m' вложенные 'VLOOKUP'. –

+0

Насколько важно, что это делается с помощью 'VLOOKUP'? Определенная пользователем функция VBA могла бы все это сделать, если вы захотите. – Mikegrann

ответ

2

Вот функции, определенной пользователем подход, который я упомянул (адаптировано из другого ВПР-варианта Я уже сделал):

' 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 

Это будет искать первый столбец в заданном диапазоне для заданного значения (такого же, как ВПР), но возвращаю значения в указанном номере столбца каскадного. Он вернет # N/A, если совпадений не найдено, и #REF, если для номера столбца указано недопустимое значение (например, вы выбираете столбец 5, но только столбец с четырьмя столбцами).

Если вы не знаете о пользовательских функциях, вы можете просто скопировать этот код VBA в VBE для модуля в вашей книге. Нажмите Alt + F11, перейдите в Insert > Module в верхней части экрана, затем вставьте этот код в пустой файл, который открывается. Когда вы переходите на сохранение, вам нужно сохранить свою книгу как Macro-Enabled (.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

@Quinn Да, эта строка неправильная. Я забыл изменить имя переменной при рефакторинге кода для использования в формуле массива. Он должен читать 'VLOOKUP_MANY_ARRAY = CVErr (xlErrRef)'. Я не уверен, что это именно то, что у вас было (это не код ошибки, который я ожидал бы), но это определенно проблема, которая теперь исправлена. – Mikegrann

0

RE-EDIT:

Вам может понадобиться написать user defined function или написать макрос (код на той же ссылке)

+0

Извините, что я должен был быть более ясным в рисках. Журналы рисков записываются на отдельных строках, поэтому один проект может иметь 5+ рисков на разных линиях. Я не думаю, что объединение Vlookups может работать с тем, как мои данные сейчас. Пожалуйста, поправьте меня, если я ошибаюсь – Quinn

+0

@Quinn. Вы указали отношения 1: m, поэтому я думаю, что это было уже довольно ясно. – Mikegrann

+0

Я отредактировал свой ответ, надеюсь, что он сработает для вас –