Возникли проблемы при создании пользовательской функции поиска в Excel. Проблема с совпадающими и объединенными диапазонами - PullRequest
2 голосов
/ 10 января 2009

У меня возникли проблемы с моей большой таблицей. Я добавляю много необработанных данных в таблицу данных, а затем делаю несколько поисков по данным. Используя встроенные функции, я придумал

=IF(ISNA(INDEX(Data!$L$7:$L$1100,MATCH(Data!$I$2&$B$199&$B29&Data!$J$5,Data!$K$7:$K$1100&Data!$J$7:$J$1100&Data!$I$7:$I$1100&Data!$N$7:$N$1100,0))),"0",INDEX(Data!$L$7:$L$1100,MATCH(Data!$I$2&$B$199&$B29&Data!$J$5,Data!$K$7:$K$1100&Data!$J$7:$J$1100&Data!$I$7:$I$1100&Data!$N$7:$N$1100,0)))

Не красиво! По сути, он выполняет один и тот же поиск дважды, беря 4 переменные и сопоставляя их с 4 сцепленными массивами, а затем использует точку в качестве индекса для требуемого значения.

У меня есть 8 из них (немного разные) в каждом ряду из 4 листов и 96 рядов на каждом листе. Редактировать их - это боль!

Из-за огромного роста набора данных в этом месяце внешние полосы (x1100) были превзойдены (извлеченный урок, больших никогда не бывает достаточно). К сожалению, ограничения функции не позволят мне использовать L: L или что-нибудь полезное.

Я пытался переписать код как пользовательскую функцию, в которой я могу передать 4 переменные и получить ответ, но потерпел неудачу при объединении массивов.

Я дал диапазоны, перечисленные выше, в исходных именах функций, чтобы упростить задачу (и расширил их, чтобы использовать гораздо более широкие значения диапазонов), чтобы я мог переписать все функции, чтобы использовать только именованные диапазоны, но это все же оставляет меня в покое, если мне нужно изменить код.

Вот что у меня есть:

    Function Windows_Util(itma As String, env As String)

v = "Windows Server" & env & itma & ""
r = Concat(Range("Utilchassis"))
r = r & Concat(Range("Utilenv"))
r = r & Concat(Range("UtilITMA"))
r = r & Concat(Range("UtilOS"))

m = WorksheetFunction.Match(v, r, 0)

i = WorksheetFunction.Index(Range("Utilavg"), m)


If WorksheetFunction.IsNA(i) Then
    Windows_Util = 0
Else
    Windows_Util = i
End If

End Function


Function Concat(myRange As Range, Optional myDelimiter As String)

Dim r As Range

Application.Volatile

For Each r In myRange
   If Len(r.Text) Then
       Concat = Concat & IIf(Concat <> "", myDelimiter, "") & r.Text
   End If
Next

End Function

Это не работает! Он не только неправильно соединяется (каждый диапазон объединяется отдельно, а не объединяется строка за строкой), ему не нравится какой-либо тип в одном из запросов. (отладка этих вещей не легка, поскольку функция фактически завершается (в ней нет синтаксических ошибок), поэтому я не могу использовать встроенные пошаговые инструкции.

Любая помощь с благодарностью.

Надеюсь, я дал достаточно подробностей, чтобы понять, что я пытаюсь сделать.

Приветствия

Стив

Ответы [ 3 ]

1 голос
/ 11 января 2009

Я не уверен, как именно вы хотите, чтобы диапазоны были конкатинированными, но я думаю, что вам стоит взглянуть на Application.Union (Range, Range, ...) метод .

В примере из статьи:

Set bigRange = Application.Union(Range("Range1"), Range("Range2"))

См. Также статью в Daily Dose of Excel от Tushar Mehta , где он описывает следующий пользовательский метод Union (Range, Range), который может лучше обрабатывать ввод 'Nothing' (ноль):

Function Union(Rng1 As Range, Rng2 As Range) As Range
    If Rng1 Is Nothing Then
        Set Union = Rng2
    ElseIf Rng2 Is Nothing Then
        Set Union = Rng1
    Else
        Set Union = Application.Union(Rng1, Rng2)
    End If
End Function

Если вместо этого вы собираетесь использовать конкатенацию строк в диапазоне адресов, то вам нужно добавить запятые, как показывает Барровец в своем посте. Например, Range("MyRange1, MyRange2") объединит два диапазона с именами «MyRange1» и «MyRange2», в то время как разделитель пробела без запятой, такой как Range("MyRange1 MyRange2"), вернет пересечение (перекрытие) этих двух диапазонов.

Однако я бы рекомендовал избегать объединения строковых адресов и использовать вместо этого метод Application.Union(Range, Range).

Надеюсь, это поможет ...

1 голос
/ 10 января 2009

Как насчет:

r = Range("Utilchassis,Utilenv,UtilITMA,UtilOS")

Это может показаться таким же, как ваша функция Concat

0 голосов
/ 19 января 2013

Функцию MATCH сложно использовать в VBA, когда есть более одного критерия для сопоставления. То, что делается на листе - сопоставление критериев, объединенных из разных ячеек, с равным числом объединенных диапазонов, в обоих случаях с использованием оператора & - недостижимо в VBA, поскольку оператор «&» VBA принимает в качестве аргументов только строки потому что другие способы объединения диапазонов в VBA, функции Union и назначения через множественные адресные аргументы Range, похоже, не создают массив, который будет работать с функцией соответствия VBA.

Однако этого же эффекта можно добиться, создав формулу рабочего листа, объединяющую сравнения MATCH и массива, а затем выполнив формулу с помощью функции VBA Evaluate. См. Мой ответ на этот вопрос , чтобы узнать, как работает этот подход.

Следующий код по существу повторно реализует исходную формулу рабочего листа, предоставленную Стивом, независимо от ранее названных диапазонов в рабочем листе или от значений критериев, указанных в коде (в отличие от конкретных ячеек в рабочем листе). Он написан как подпрограмма, но может быть легко преобразован в пользовательскую функцию.

  Sub x4match()

    Dim adr1 As String, adr2 As String, adr3 As String, adr4 As String
    Dim Rng1 As Range, Rng2 As Range, Rng3 As Range, Rng4 As Range
    Dim name1 As String, name2 As String, name3 As String, name4 As String
    Dim idx As Variant
    Dim resultRng As Range
    Dim result As Variant

    With ThisWorkbook.Worksheets("Data")

       adr1 = "I2"
       adr2 = "B199"
       adr3 = "B29"
       adr4 = "J5"

       Set Rng1 = .Range("K7:K1100")
       Set Rng2 = .Range("J7:J1100")
       Set Rng3 = .Range("I7:I1100")
       Set Rng4 = .Range("N7:N1100")
       Set resultRng = .Range("L7:L1100")

       .Names.Add Name:="name1", RefersTo:=Rng1
       .Names.Add Name:="name2", RefersTo:=Rng2
       .Names.Add Name:="name3", RefersTo:=Rng3
       .Names.Add Name:="name4", RefersTo:=Rng4

       idx = Evaluate("IFERROR( MATCH(1, --(" & adr1 & "=name1) * --(" & _
                     adr2 & "=name2) * --(" & adr3 & "=name3) * --(" & _ 
                     adr4 & "=name4), 0), 0)")

       If idx <> 0 Then
          result = .Cells(resultRng.Row + idx - 1, resultRng.Column).Value
       End If

      .Names("name1").Delete
      .Names("name2").Delete
      .Names("name3").Delete
      .Names("name4").Delete

    End With

  End Sub
...