Vba excel vlookup обработка ошибок

I developed the code for a vlookup but I’m having trouble with error handling. The values of a column on Sheet1 are being looked up in a column on Sheet2. The results are displayed on Sheet3.

There are 2 instances where there would be errors:

  1. If cells in the lookup value column are blank

  2. If the lookup values aren’t in the table array

If the cells of the lookup values are blank, I want the results to display blanks. If the lookup values aren’t blank but are missing from the table array, I want the cells to display «Missing». Right now the code is set up to just always give blanks.

The code I have so far is below. I’m an excel formula pro and a VBA novice. Any help would be appreciated!

On Error Resume Next

Dim Fund_Row As Long
Dim Fund_Clm As Long

Table1 = rangeA  'Column on Sheet1
Table2 = rangeB  'Column on Sheet2

Fund_Row = Sheets("Sheet3").Range("B2").Row
Fund_Clm = Sheets("Sheet3").Range("B2").Column

For Each cl In Table1
Sheets("Sheet3").Cells(Fund_Row, Fund_Clm)=    

Application.WorksheetFunction.VLookup(cl, Table2, 1, False)
Fund_Row = Fund_Row + 1
Next cl

End Sub

asked Jan 8, 2018 at 7:58

reece's user avatar

1

For catching #NA! error, you could use just IFNA worksheet function. Just like that:

With Application.WorksheetFunction
    Sheets("Sheet3").Cells(Fund_Row, Fund_Clm) = .IfNa(.VLookup(cl, Table2, 1, False), "Missing")
End With

You can deal with blanks two way. The simple is to add blanks to the table you are vlookuping. Little more sophisticated is add another if in code:

If cl.value = "" then ...

answered Jan 8, 2018 at 8:33

MarcinSzaleniec's user avatar

MarcinSzaleniecMarcinSzaleniec

2,2361 gold badge7 silver badges22 bronze badges

 

LightZ

Пользователь

Сообщений: 1748
Регистрация: 22.12.2012

Всем привет.  
Подскажите, пожалуйста, как обойти ошибку #Н/Д с помощью VBA?  
Использую VLookup.  
Код ниже:  
Dim s, i As Integer  
For i = 1 To 5  
s = Application.WorksheetFunction.VLookup(Range(«A» & i), Range(«C» & i), 1, 0)  
Range(«B» & i) = s  
Next  

  Всем заранее спасибо!

Киса, я хочу Вас спросить, как художник — художника: Вы рисовать умеете?

 

ну, приложите небольшой файл-пример (нам так легче будет для вас код писать и тестировать)  

  P.S. А так, что в VBA, что на обычно листе #Н/Д обходится EНД (ISNA). Почитайте в Справке

 

LightZ

Пользователь

Сообщений: 1748
Регистрация: 22.12.2012

{quote}{login=Ластик}{date=21.01.2012 03:59}{thema=}{post}ну, приложите небольшой файл-пример (нам так легче будет для вас код писать и тестировать)  

  P.S. А так, что в VBA, что на обычно листе #Н/Д обходится EНД (ISNA). Почитайте в Справке{/post}{/quote}  
ну в экселе можно исправить с помощью ЕСЛИ и ЕНД, а как это сделать в VBA — не знаю.  
файл прилагаю.

Киса, я хочу Вас спросить, как художник — художника: Вы рисовать умеете?

 

LightZ

Пользователь

Сообщений: 1748
Регистрация: 22.12.2012

к примеру если изменить код, следующим образом:  
s = Application.WorksheetFunction.VLookup(Range(«A:A»), Range(«C:C»), 1, 0)  
Range(«B:B») = s  

  на те строки, которые не совпадают с помощью ВПР — получаться ошибки #Н/Д.  
как с помощью VBA заменить #Н/Д на «0» ?  

  И как обойти ошибку VBA в вышеуказанном примере?

Киса, я хочу Вас спросить, как художник — художника: Вы рисовать умеете?

 

сейчас посмотрим, но у меня такое чувство, что вы не знаете, как работает ВПР на листе. В Справке написано:  

  ВПР(искомое_значение, таблица, номер_столбца, [интервальный_просмотр])

  т.е. (что искать, в каком диапазоне ячеек искать, номер столбца, интервальный просмотр)  

  А вы в коде какую-то ерунду пишите

 

Hugo

Пользователь

Сообщений: 23100
Регистрация: 22.12.2012

Как-то всё глупо… Но по существу вопроса — в этом примере можно так:  

    Sub f()  
Dim s, i As Integer  
On Error Resume Next  
For i = 1 To 5  
s = Application.WorksheetFunction.VLookup(Range(«A» & i), Range(«C» & i), 1, 0)  
Range(«B» & i) = s  
s = Empty  
Next  
End Sub

 

LightZ

Пользователь

Сообщений: 1748
Регистрация: 22.12.2012

{quote}{login=Hugo}{date=21.01.2012 04:31}{thema=}{post}Как-то всё глупо… Но по существу вопроса — в этом примере можно так:  

    Sub f()  
Dim s, i As Integer  
On Error Resume Next  
For i = 1 To 5  
s = Application.WorksheetFunction.VLookup(Range(«A» & i), Range(«C» & i), 1, 0)  
Range(«B» & i) = s  
s = Empty  
Next  
End Sub{/post}{/quote}  

  Спасибо, сам не додумался, писал просто On Error Resume Next, но ничего не получалось.  

  Почему глупо? я выложил просто минимальный пример ВПР, только необходимо заменить #Н/Д на 0 с помощью VBA  
Формулой бы это выглядело так — =ЕСЛИ(ЕНД(ВПР(A:A;C:C;1;0));0;(ВПР(A:A;C:C;1;0)))

Киса, я хочу Вас спросить, как художник — художника: Вы рисовать умеете?

 

LightZ

Пользователь

Сообщений: 1748
Регистрация: 22.12.2012

{quote}{login=Ластик}{date=21.01.2012 04:29}{thema=}{post}сейчас посмотрим, но у меня такое чувство, что вы не знаете, как работает ВПР на листе. В Справке написано:  

  ВПР(искомое_значение, таблица, номер_столбца, [интервальный_просмотр])

  т.е. (что искать, в каком диапазоне ячеек искать, номер столбца, интервальный просмотр)  

  А вы в коде какую-то ерунду пишите{/post}{/quote}  
хех, почему ж ерунду? :)  
в примере диапазон А:А это искомое значение, С:С это таблица, 1 это № столбца  

  Что же тут не понятно ?:)

Киса, я хочу Вас спросить, как художник — художника: Вы рисовать умеете?

 

Hugo

Пользователь

Сообщений: 23100
Регистрация: 22.12.2012

Что за А:А, С:С?  
ОДНО искомое значение ищем в ДИАПАЗОНЕ!  
А у Вас в коде одно значение ищется в одном значении, а в примере формулы диапазон в диапазоне… Ерунда!

 

Юрий М

Модератор

Сообщений: 60335
Регистрация: 14.09.2012

Контакты см. в профиле

LightZ, для начала загляните сюда: 

http://www.planetaexcel.ru/tip.php?aid=26

поймёте свою ошибку. Также полезно почитать встроенную справку по этой функции. Особое внимание обратите на искомое значение.

 

нарисовал для вас пример, см. файл

 

Igor67

Пользователь

Сообщений: 3704
Регистрация: 21.12.2012

Читал, читал ну не понимаю на какой Х? ВПР?  
сравниваем по строкам, ну так почему не через простое равно?  
Sub f()  
Dim i As long  
On Error Resume Next  
For i = 1 To 5  
if cells(i,1)=cells(i,3) then    
cells(i,2)=cells(i,1)  
else: cells(i,1)=0  
Next  
End Sub

 

KuklP

Пользователь

Сообщений: 14868
Регистрация: 21.12.2012

E-mail и реквизиты в профиле.

Зря накинулись на человека. Каждый имеет право на ошибку. Пусть бы помучался. Зато потом бы ВПР на всю жизнь запомнил:-) А так быстро забудет.

Я сам — дурнее всякого примера! …

 

Hugo

Пользователь

Сообщений: 23100
Регистрация: 22.12.2012

И вообще — в коде использовать формулы листа как-то неправильно (на мой взгляд).  
Хотя я тоже иногда использую — но это скорее из лени.  
В данном случае — на каждом шаге цикла формула будет прогонять через себя весь диапазон и искать нужное значение. Пусть это быстро встроенным механизмом Экселя, но думаю быстрее было бы сперва взять диапазон в массив, потом загнать его в словарь (один перебор массива), затем искать в словаре одним проходом по массиву из столбца.  
Думаю, что цикл поиска в словаре быстрее поиска ВПРом по диапазону.

 

а я вообще не понял, что человек хочет сделать. Спрашивает про ВПР, ответил про ВПР ))  

  По-моему, человеку нужно было сделать так  
1) создать пример в файле  
2) создать тему с вопросом «ааа, ребятаааа, помогите в VBA …. (дальше идут вариации) …. сравнить 2 столбца…. подтянуть данные из одного столбца в другой… и т.д.»  

  а мы бы уже что-нибудь придумали, а то «НД в ВПР», а что такое ВПР сам не знает

 

Hugo

Пользователь

Сообщений: 23100
Регистрация: 22.12.2012

А если делать, как в примере Ластика, так там в каждой ячейке по два ВПР() отрабатывают… не всегда, но в большинстве случаев.  
Или вторая формула берёт уже вычисленное значение? Кто б потестил… :)

 

Юрий М

Модератор

Сообщений: 60335
Регистрация: 14.09.2012

Контакты см. в профиле

{quote}{login=KukLP}{date=21.01.2012 04:56}{thema=}{post}Зря накинулись на человека. {/post}{/quote}Серж, никто не набрасывался — ему говорят про его ошибку, а в ответ снова неверное представление формулы и «Что же тут не понятно ?:)»    
Вот и спрашивается: человеку нужна помощь или зашёл нас вразумить?

 

Игорь, формулистов на тебя не хватает ))

 

Hugo

Пользователь

Сообщений: 23100
Регистрация: 22.12.2012

А кстати меня это давно интересует — два раза ВПР отрабатывает или один? Хотя тут это офф…

 

KuklP

Пользователь

Сообщений: 14868
Регистрация: 21.12.2012

E-mail и реквизиты в профиле.

{quote}{login=Юрий М}{date=21.01.2012 05:04}{thema=Re: }{post}{quote}{login=KukLP}{date=21.01.2012 04:56}{thema=}{post}Зря накинулись на человека. {/post}{/quote}Серж, никто не набрасывался — ему говорят про его ошибку, а в ответ снова неверное представление формулы и «Что же тут не понятно ?:)»    
Вот и спрашивается: человеку нужна помощь или зашёл нас вразумить?{/post}{/quote}  
Я ж и говорю, пусть сам прочувствует:-) Вон у Володи в подписи: «И то, что понято с трудом, то мне дороже..»

Я сам — дурнее всякого примера! …

 

{quote}{login=Hugo}{date=21.01.2012 05:07}{thema=}{post}А кстати меня это давно интересует — два раза ВПР отрабатывает или один? Хотя тут это офф…{/post}{/quote}  

  я думаю 2 раза, потому как нельзя проверить будет ли НД не рассчитав первый ВПР.  
Просто если таблица до 1000 строк, это не принципиально. В каждом рабочем файле (в моём например), всегда куча формул было разных. Это же не чистая база данных, там расчёты, обычно, идут.

 

LightZ

Пользователь

Сообщений: 1748
Регистрация: 22.12.2012

Всем спасибо!  
блин, да знаю я как пользоваться ВПР :)  
вот только-что накидал — во вложении пример ВПР в VBA

Киса, я хочу Вас спросить, как художник — художника: Вы рисовать умеете?

 

Hugo

Пользователь

Сообщений: 23100
Регистрация: 22.12.2012

В 10 раз быстрее:  

  Sub tt()  
   Dim tm!: tm = Timer  
   Dim a(), b(), i&, ii&  
   b = Sheets(2).UsedRange.Value  
   With CreateObject(«Scripting.Dictionary»)  
       For i = 1 To UBound(b)  
           .Item(b(i, 1)) = i  
       Next  
       a = Sheets(1).UsedRange.Columns(1).Value  
       ReDim c(1 To UBound(a), 1 To 3)  
       For i = 1 To UBound(a)  
           If .exists(a(i, 1)) Then  
               ii = .Item(a(i, 1))  
               c(i, 1) = b(ii, 2)  
               c(i, 2) = b(ii, 3)  
               c(i, 3) = b(ii, 4)  
           Else  
               c(i, 1) = «Данных в базе нет»  
               c(i, 2) = «Данных в базе нет»  
               c(i, 3) = «Данных в базе нет»  

             End If  
       Next  

     End With  
   Sheets(1).[B1:D1].Resize(i — 1) = c
   Debug.Print Timer — tm  
End Sub  

      Сравните время:  
0,15625    
0,140625    
0,15625    
0,015625    
0,03125    
0,015625

 

LightZ

Пользователь

Сообщений: 1748
Регистрация: 22.12.2012

Спасибо Hugо, так я ещё не умею.  
буду разбирать код ;)

Киса, я хочу Вас спросить, как художник — художника: Вы рисовать умеете?

 

ну, и я свой вариант на массивах

 

Hugo

Пользователь

Сообщений: 23100
Регистрация: 22.12.2012

Алгоритм выше я уже описАл, он не ноухау — диапазон в массив, его в словарь, второй в массив, проверяем по словарю.  
Плюс создал массив для результатов — хотя можно было сделать массив а пошире и складывать в него, его же назад и выгрузить. Но тогда затрутся/перепишутся данные первого столбца, что лишняя работа.

 

LightZ

Пользователь

Сообщений: 1748
Регистрация: 22.12.2012

{quote}{login=Ластик}{date=21.01.2012 06:19}{thema=}{post}ну, и я свой вариант на массивах{/post}{/quote}  
спасибо за комментарии в коде

Киса, я хочу Вас спросить, как художник — художника: Вы рисовать умеете?

 

Сравнил по скорости мой код с Hugo, что-то у меня выходил либо 0,0078125 либо 0 и там и там )  

  P.S. На больших объёмах Словарь выиграет, на небольших незаметно.

 

Hugo

Пользователь

Сообщений: 23100
Регистрация: 22.12.2012

Ну на таком объёме тестить конечно несерьёзно — я вообще удивлён, что у меня такая разница получилась. Там может основная потеря времени на «инициализацию» формулы уходит, чего в моём коде нет.  
Точнее тысяч на 40 строк проверять.

 

LightZ

Пользователь

Сообщений: 1748
Регистрация: 22.12.2012

#30

21.01.2012 18:38:28

{quote}{login=Ластик}{date=21.01.2012 06:30}{thema=}{post}Сравнил по скорости мой код с Hugo, что-то у меня выходил либо 0,0078125 либо 0 и там и там )  

  P.S. На больших объёмах Словарь выиграет, на небольших незаметно.{/post}{/quote}  
добавил и свой вариант, для сравнения скорости, на данный момент разницы практически нет.  
но наверное нужно потестить на больших массивах…

Прикрепленные файлы

  • post_299397.xlsm (24.52 КБ)

Киса, я хочу Вас спросить, как художник — художника: Вы рисовать умеете?

I developed the code for a vlookup but I’m having trouble with error handling. The values of a column on Sheet1 are being looked up in a column on Sheet2. The results are displayed on Sheet3.

There are 2 instances where there would be errors:

  1. If cells in the lookup value column are blank

  2. If the lookup values aren’t in the table array

If the cells of the lookup values are blank, I want the results to display blanks. If the lookup values aren’t blank but are missing from the table array, I want the cells to display «Missing». Right now the code is set up to just always give blanks.

The code I have so far is below. I’m an excel formula pro and a VBA novice. Any help would be appreciated!

On Error Resume Next

Dim Fund_Row As Long
Dim Fund_Clm As Long

Table1 = rangeA  'Column on Sheet1
Table2 = rangeB  'Column on Sheet2

Fund_Row = Sheets("Sheet3").Range("B2").Row
Fund_Clm = Sheets("Sheet3").Range("B2").Column

For Each cl In Table1
Sheets("Sheet3").Cells(Fund_Row, Fund_Clm)=    

Application.WorksheetFunction.VLookup(cl, Table2, 1, False)
Fund_Row = Fund_Row + 1
Next cl

End Sub

asked Jan 8, 2018 at 7:58

reece's user avatar

1

For catching #NA! error, you could use just IFNA worksheet function. Just like that:

With Application.WorksheetFunction
    Sheets("Sheet3").Cells(Fund_Row, Fund_Clm) = .IfNa(.VLookup(cl, Table2, 1, False), "Missing")
End With

You can deal with blanks two way. The simple is to add blanks to the table you are vlookuping. Little more sophisticated is add another if in code:

If cl.value = "" then ...

answered Jan 8, 2018 at 8:33

MarcinSzaleniec's user avatar

MarcinSzaleniecMarcinSzaleniec

2,2361 gold badge7 silver badges22 bronze badges

Instead of WorksheetFunction.Vlookup, you can use Application.Vlookup. If you set a Variant equal to this it returns Error 2042 if no match is found. You can then test the variant — cellNum in this case — with IsError:

Sub test()
Dim ws As Worksheet: Set ws = Sheets("2012")
Dim rngLook As Range: Set rngLook = ws.Range("A:M")
Dim currName As String
Dim cellNum As Variant

'within a loop
currName = "Example"
cellNum = Application.VLookup(currName, rngLook, 13, False)
If IsError(cellNum) Then
    MsgBox "no match"
Else
    MsgBox cellNum
End If
End Sub

The Application versions of the VLOOKUP and MATCH functions allow you to test for errors without raising the error. If you use the WorksheetFunction version, you need convoluted error handling that re-routes your code to an error handler, returns to the next statement to evaluate, etc. With the Application functions, you can avoid that mess.

The above could be further simplified using the IIF function. This method is not always appropriate (e.g., if you have to do more/different procedure based on the If/Then) but in the case of this where you are simply trying to determinie what prompt to display in the MsgBox, it should work:

cellNum = Application.VLookup(currName, rngLook, 13, False)
MsgBox IIF(IsError(cellNum),"no match", cellNum)

Consider those methods instead of On Error ... statements. They are both easier to read and maintain — few things are more confusing than trying to follow a bunch of GoTo and Resume statements.

Instead of WorksheetFunction.Vlookup, you can use Application.Vlookup. If you set a Variant equal to this it returns Error 2042 if no match is found. You can then test the variant — cellNum in this case — with IsError:

Sub test()
Dim ws As Worksheet: Set ws = Sheets("2012")
Dim rngLook As Range: Set rngLook = ws.Range("A:M")
Dim currName As String
Dim cellNum As Variant

'within a loop
currName = "Example"
cellNum = Application.VLookup(currName, rngLook, 13, False)
If IsError(cellNum) Then
    MsgBox "no match"
Else
    MsgBox cellNum
End If
End Sub

The Application versions of the VLOOKUP and MATCH functions allow you to test for errors without raising the error. If you use the WorksheetFunction version, you need convoluted error handling that re-routes your code to an error handler, returns to the next statement to evaluate, etc. With the Application functions, you can avoid that mess.

The above could be further simplified using the IIF function. This method is not always appropriate (e.g., if you have to do more/different procedure based on the If/Then) but in the case of this where you are simply trying to determinie what prompt to display in the MsgBox, it should work:

cellNum = Application.VLookup(currName, rngLook, 13, False)
MsgBox IIF(IsError(cellNum),"no match", cellNum)

Consider those methods instead of On Error ... statements. They are both easier to read and maintain — few things are more confusing than trying to follow a bunch of GoTo and Resume statements.

Поискав по рунету материал на тему обработки ошибок в VBA, не увидал на первых двух страницах результатов поиска чего-то, что мне понравилось. Может плохо смотрел, но решил написать на эту тему свою статью.

Простите, но — немного словоблудия :)

Ошибки в программе

Ошибки времени исполнения программы возникают, когда среда программирования не может выполнить то, что вы хотите. Таких ситуаций может быть много. Например:

  1. Вы обращаетесь к объекту по имени, а объекта с таким именем в коллекции нет

  2. Вы хотите выделить ячеку на одном листе, а этот лист в данный момент не является активным (типичнейшая ошибка новичков в Excel VBA)

  3. Вы хотите удалить отфильтрованные автофильтром строки, а фильтр вообще не вернул записей и удалять нечего

  4. Вы ссылаетесь на элемент массива, который находится за пределами его границ.

  5. Вы пытаетесь присвоить переменной значение, которое оно не может хранить. Например, переменной типа Long нельзя присвоить строковую константу или переменной типа Integer присвоить знанчение превышающее число 32767.

На любую из этих и сотни других ситуаций среда выполнения реагирует стандартно — прерывает ход выполнения программы на том операторе, где возникла ошибка или, как ещё принято говорить, исключение. На экран выводится информация о возникшей ошибке и предлагаются стандартные варианты для продолжения работы:

  • Continue (продолжить) — этот пункт во время возникновения ошибки всегда не активен. Он активен, когда по ходу выполнения программы вы использовали оператор Stop. Кстати это очень полезный оператор для отладки программы.

  • End (завершить) — завершение исполнения программы

  • Debug (отладка) — переход в режим отладки, в котором можно посмотреть, на каком операторе возникла ошибка, что содержат переменные, можно даже перетащить жёлтую полоску, подсвечивающую текущий оператор, назад, и модифицировать знанчение переменных через окно Immediate window (впрочем это экзотика). В общем случае кнопка Debug позволяет посмотреть, где случилась ошибка и попытаться понять почему так случилось.

Если вы — автор программы, в которой случилась ошибка, то вы, должно быть, в начале будете рады увидеть подобное окно, ибо только так вы сможете отловить основные ошибки, скрытые в вашем коде. Однако, если эту ошибку видит пользователь, то для него это, мягко говоря, безрадостное и малопонятное зрелище. Ещё хуже, если за эту программу вам заплатили деньги. Поэтому в среде худо-бедно профессиональных программистов принято предусматривать обработку ошибок в своих программах.

Почему вообще в коде возникают ошибки?

  1. Много ошибок во время написания кода возникает по невнимательности или не совсем адекватного понимания того, что делаешь. Таких ошибок, как правило, очень много, особенно у начинающих программистов, но эти ошибки довольно легко отловить и исправить, так как, пока вы их не исправите, ничего не работает. Ну, например, вы должны извлечь данные из 5-го столбца, а вы извлекаете из 6-го, а их там банально нет. Ясно, что вы это очень быстро заметите.

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

  3. Тонкие логические ошибки. Чем сложнее программа, тем больше шансов, что модель задачи в вашей голове, ваша программа и реальность не совсем согласованы между собой. Пока вы не достигните достаточного погружения в задачу вы такие ошибки не найдёте и не исправите. Порой на это уходит много времени. Но это характерно для сложных задач.

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

Задачи механизмов обработки ошибок

  1. Обеспечить стабильную работу программы. Возникновение ошибки, появление которой вы не предусмотрели, приведёт в большинстве случаев к аварийному завершению всей программы или её части. При определенном уровне подобных ситуаций это ведёт к тому, что программой пользоваться становится невозможно.

  2. Информирование. Мало обработать ошибку и предотвратить завершение программы. Надо ещё и адекватно проинформировать пользователя о причинах нестандартного поведения программы. Частно причиной ошибок в программе являются некорректные действия пользователя, поэтому важно сообщать ему о них.

  3. Защита данных от повреждения. Программа обязана защищать от непреднамеренных повреждений результаты своей или пользовательской работы. Деструктивные действия должны быть снабжены соответствующими предупредительными диалоговыми окнами. Часто ошибка, не обработанная должным образом может повредить нужные данные.

Файл примера

Скачать

Код без обработки ошибок

Вот простой пример с потолка. Если вызвать Example_00, то она прекрасно отработает без ошибок и вернёт это:

В функцию GetCalories передаётся строка с блюдом, а она должна вернуть его калорийность, сверившись с таблицей в A1:B7.

Давайте поищем слабые места в этом коде. Первое, что должно прийти в голову — если мы ищем, то, что произойдёт, если мы не найдём? А произойдёт, конечно же, ошибка. Её инициирует метод Match.


Ещё одно слабое место этой подпрограммы: функция возвращает вещественный тип Double, и даже, если поиск оказался удачным, то в Cells(intRow, 2) может случайно находиться текстовая строка, а потому, когда вы числовому типу попытаетесь присвоить строковый тип, также произойдёт ошибка. И, если вы второй ошибки сможете избежать за счёт дополнительного оператора if с проверкой через IsNumber(), то избежать первой ошибки таким способом нельзя. Что же делать? А вот тут на сцену выходят операторы обработки ошибок.

Есть 2 подхода к обработке ошибок: автономный подход и выносной. Эти термины я придумал только что, чтобы проще было их обсуждать.

Автономный подход

Смысл автономного подхода в том, чтобы не выносить сор из избы. Если в подпрограмме возникла ошибка, то мы должны предположить, на каком месте она возникнет и поджидать её там с дубиной. С ошибкой, в этом случае, разбираются обычно в операторе, идущем сразу после потенциально опасного места. Давайте смотреть, как это может выглядеть:

Итак, что тут сделано:

  1. Сразу после объявления функции GetCalories_v1 идёт оператор on error resume next, который в случае возникновения в каком-либо месте ошибки, предписывает VBA просто передавать управление на следующий оператор, идущий после ошибочного.

  2. Мы объявили переменные. Необъявленные переменные получают тип Variant и значение по умолчанию Empty. Объявленные переменные числовых типов инициируются нулём, строковые — пустой строкой, то есть я наперёд знаю, что они содержат, а это хорошо для обработки ошибок.

  3. На вызове метода WorksheetFunction.Match у нас возникает ошибка, так как искомого значения в таблице нет. А это, между прочим, был оператор присваивания ( = ). Прежде, чем левой части оператора присваивания (intRow) что-то будет присвоено, необходимо вычислить правую часть оператора присваивания (WorksheetFunction.Match…), а поскольку в процессе этого вычисления возникает ошибка, то переменная intRow остаётся такой, какой была! А, как я уже сказал, VBA автоматически её инициализирует нулём до начала исполнения подпрограммы. Получается, что, если в этом операторе возникнет ошибка, то в intRow будет ноль. Если ошибки во время поиска не возникнет, то ноля там не будет ни при каких раскладах, так как строки на листе нумеруются с единицы.

  4. И вот этот ноль мы и контролируем, добавляя оператор If. Если intRow больше нуля, то WorksheetFunction.Match отработала штатно, а если нет — то работу подпрограммы надо прерывать, но об этом чуть позже.

  5. Далее мы помним, что Cells(intRow, 2) может теоретически вернуть строковое значение, которое вызовет ошибку Type missmatch при присвоении переменной типа Double (GetCalories_v1), поэтому мы вставляем дополнительную проверку промежуточной переменной varTemp тому, что она числовая. И если это так, то присваиваем GetCalories_v1 значение из varTemp.

  6. В случае возникновения любой ошибки внутри GetCalories_v1 она просто вернёт ноль. Почему ноль? Потому что переменная GetCalories_v1 тоже инициализируется нулём и об этом не надо заботиться, а в случае ошибки она останется в неприкосновенности.

  7. Соответственно родительский код (в нашем случае его роль играет процедура Example_01) должен проверить, а не вернёт ли GetCalories_v1 ноль, и быть готовым к этой ситуации.

  8. А вот теперь тонкий момент, который не все понимают. Почему я использовал промежуточные переменные intRow и varTemp? Вроде бы есть очевидный ответ — чтобы не вычислять значение выражений с Match и Cells 2 раза. Отчасти это, конечно, так. Но это, в данном случае, не главная причина. Главная причина в том, что такой код

    вызовет неправильное поведение программы. Если у нас Match вызовет исключение, то VBA передаст управление на СЛЕДУЮЩИЙ оператор, а следующий оператор в данном случае это то, что идёт после Then — присваивание переменной varTemp значения. Таким образом наша проверка на наличие ошибки сработает с точностью до наоборот, передав управление в ту часть кода, которая должна быть защищена от ситуации, когда Match не нашла строку в таблице. Вот почему важно в операторе If не иметь ничего такого, что могло бы вызвать ошибку.

  9. Как видите, в этом подходе мне зачастую даже нет необходимости проверять объект Err, чтобы понять, что произошла ошибка, так как я ориентируюсь на то, что промежуточные переменные остаются неинициализированными, что является показателем наличия ошибки.

Выносной подход

Данный метод основан на том, что, когда возникает ошибка, то VBA передаёт управление на специальный участок кода — обработчик ошибок, который обычно размещают в конце подпрограммы. Это может выглядеть так:

Обратите внимание, что:

  1. Оператор on error теперь в случае ошибки предписывает передавать управление на метку ErrorHandler, которая объявлена в конце кода процедуры GetCalories_v2

  2. В коде мы никак не заботимся о каких-либо проверках. Возникла ошибка? Иди на метку — там разберутся.

  3. Если ошибки не случилось, то, чтобы программа не стала исполнять строчки, предназначенные для обработки ошибок, перед меткой ErrorHandler обычно ставят оператор Exit Sub или Exit Function (в зависимости от типа подпрограммы).

  4. Принципиальный момент — наличие оператора On Error Resume Next сразу после метки ErrorHandler. Дело в том, что после того, как вы перешли на метку ErrorHandler, очень опасно иметь действующим оператор On Error GoTo ErrorHandler, так как, если у вас в обработчике ошибки случится любая ошибка, то управление будет передано опять на метку и, как нетрудно понять, образуется бесконечный цикл. Поэтому сразу после метки мы возможность возникновения цикла ликвидируем оператором On Error Resume Next.

Что лучше?

Какой метод лучше применять зависит от ваших предпочтений и конкретных ситуаций. Грамотную обработку ошибок можно сделать и так и эдак. Вот несколько соображений по преимуществам и недостакам данных подходов:

Автономный подход

Преимущества Недостатки
Есть возможность точно идентифицировать каждую конкретную проблему (если вы её предусмотрели), возникающую во время исполнения, что позволит вам дать самые точные инстркции пользователю для предотвращения появления исключения в будущем. Достаточно трудоёмок, так как подразумевает наличие большого количества проверок в коде. Каждое потенциально опасное действие должно быть снабжено соответствующим оператором If, в котором контролируется значение переменной или код ошибки.
Надо хорошо представлять себе ситуации, где могут возникнуть ошибки, в противном случае ряд ошибок вы просто не заметите на этапе отладки.
Необходимо больше кода, а также требуется опыт и фантазия.
Необходимо больше промежуточных переменных

Выносной подход

Преимущества Недостатки
Ни одна ошибка не проскочит незамеченной. Не смотря на то, что вы перехватите все ошибки, отреагировать на них правильно затруднительно, так как вы, по большому счёту, не знаете, на каком операторе произошла ошибка и почему.
Удобнее организовывать централизованный сбор логов по ошибкам в приложении. Однозначно, фаворит для больших проектов.

Кратко пробежимся по операторам, функциям и объектам VBA, которые предназначены для обработки ошибок времени исполнения программы.

Операторы

On Error { GoTo label | Resume Next | GoTo 0 }

Оператор on error управляет тем, на какой участок вашего кода будет передано управление в случае возникновения ошибки. Данный оператор можно вставить в любое место вашей программы или подпрограммы. Есть 3 варианта:

  1. On error goto label — после того, как этот оператор выполнен, ошибка, возникшая на других операторах программы приведёт к переходу на метку label.

  2. On error resume next — после такого оператора, VBA будет игнорировать возникшую ошибку и передавать управление на следующий оператор, стоящий за тем, в котором возникла ошибка.

  3. On error goto 0 — это режим по-умолчанию. В случае возникновения ошибки данный режим приведёт к появлению на экране стандартного обработчик ошибок VBA с кнопками End и Debug.

Resume { label | Next | [0] }

Данный оператор возобновляет выполнение программы. Применяется в выносном методе обработки ошибок.

  1. resume label— возобновление с метки label

  2. resume next — возобновление со следующего оператора

  3. resume или resume 0 — возобновление с оператора, вызвавшего ошибку. Это имеет смысл, если вы устранили ошибку в своём обработчике. На мой взгляд, на практике такое применяется крайне редко.

Goto label

Переход на метку. Может пригодиться, однако, использование меток в коде для чего-то большего, чем обработка ошибок, считается страшным моветоном.

Exit { Do | For | Function | Sub }

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

Объект Err

  1. Err — глобальный объект (его не надо объявлять, а можно сразу пользоваться), который содержит информацию о последней ошибке, случившейся в вашей программе. Проверяя Err сразу после возникновения исключения или после ситуации, которая могла привести к исключению, вы можете понять, что имело место на самом деле.

  2. Свойство Err.Number — содержит числовой код ошибки, по которому их различают в программе. Поскольку Number — свойство по умолчанию, то вы можете его опускать, то есть Err и Err.Number — это эквиваленты. Значение ноль говорит о том, что ошибки не произошло.

  3. Err.Description — содержит англоязычное краткое описание ошибки

  4. Err.Source — возвращает имя модуля, в котором возникла ошибка

  5. Err.Clear — сбрасывает последнюю ошибку. Err сбрасывается также при выполнении оператором Resume, Exit (любого типа кроме Do и For) и On Error.

  6. Err.Raise — искусственно вызывает исключение указанного в переданном параметре типа. Можно использовать для тестирования вашей подсистемы обработки ошибок.

P.S.

Лично я привык в своих программах использовать автономный подход и, возможно, поэтому я не совсем осознаю все преимущества выносного подхода. Буду рад прочесть в комментариях ваше мнение на этот счёт. Тема обработки ошибок данной статьёй, конечно, быть исчерпана не может, но она послужит вам хорошей стартовой точкой в этом важном деле.

Читайте также:

  • Работа с объектом Range

  • Работа с объектом Range (часть 2)

  • Sheet happens

  • Поиск границ текущей области

  • Массивы в VBA

  • Структуры данных и их эффективность

  • Автоматическое скрытие/показ столбцов и строк

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

В Excel VBA при выполнении любого набора кодов мы получаем какие-то ошибки. Некоторые из этих ошибок являются синтаксическими; некоторые из них являются неисполняемыми ошибками. Синтаксическая ошибка, сделанная пользователем, выделяется красным цветом самим Excel. Но когда возникает какая-либо другая ошибка времени выполнения, как мы с ней справимся и как выйти за рамки этого, мы рассмотрим в этой статье.

Помимо синтаксических ошибок, другие ошибки времени выполнения необходимо обрабатывать при выполнении любого набора кодов. Во-первых, позвольте мне привести пример того, как возникает другая ошибка времени выполнения. Взгляните на приведенный ниже код,

пример ошибки vba image1

Это пример кода, который при выполнении вернет то, что написано в функции msgbox. Но, как мы видим, во второй строке кода есть 4/0, что невозможно с математической точки зрения, поэтому он вернет ошибку времени выполнения. Давайте выполним приведенный выше код и увидим ошибку, которую мы получим.

пример ошибки vba image2

Это ошибка, которую мы получаем при выполнении данного кода. Теперь, как мы обрабатываем эту ошибку, производится обработкой ошибок.

Существует два метода обработки ошибок:

  1. При ошибке Перейти к и
  2. При ошибке Возобновить дальше.

Объяснение

Как объяснялось выше, мы получаем много типов ошибок в VBA. Некоторые из них являются синтаксисом, а некоторые — временем выполнения. Синтаксические ошибки уже выделены красным цветом, например, см. скриншот ниже,

пример ошибки vba 3

В то время как другие ошибки времени выполнения. По сути, Excel выполняет следующие три действия: либо показывает ошибку, либо игнорирует эту ошибку, либо показывает определенный набор инструкций. Для выполнения таких задач нам нужно давать инструкции, и это называется обработкой ошибок.

Как обрабатывать ошибки в коде VBA?

Вы можете скачать этот шаблон Excel для обработки ошибок VBA здесь — Шаблон Excel для обработки ошибок VBA

Пример №1

В качестве первого примера возьмем первый код, который мы взяли в качестве демонстрации. В приведенном выше примере мы видели, что код выдает ошибку времени выполнения во второй функции msgbox.

Напишите следующий код после открытия подфункции,

Код:

Sub Sample()
On Error Resume Next
MsgBox 4 / 2
MsgBox 4 / 0
MsgBox 4 / 1
End Sub

пример ошибки vba 1.1

Теперь, когда мы выполняем приведенный выше код, мы видим, что строка кода с ошибкой не выполняется. Excel пропускает эту строку и возобновляет работу на следующей строке.

пример ошибки vba 1.2

Существует еще один способ обработки ошибки — VBA. Заявление о переходеОператор VBA GoTo позволяет перейти к строке кода, пропуская все ошибки между ними. Существует два типа операторов GOTO: один — выбрать любой диапазон рабочего листа в указанной книге, а другой — обработчик ошибок.читать далее. Мы предоставляем Excel в качестве места назначения для перехода при обнаружении ошибки. Вместо предыдущего кода обработки ошибок мы вставили, запишите следующий код,

Код:

Sub Sample()
On Error GoTo az
MsgBox 4 / 2
MsgBox 4 / 0
MsgBox 4 / 1
End Sub

пример ошибки vba 1.3

Мы даем Excel Az в качестве пункта назначения, к которому можно перейти, если он обнаружит ошибку. Теперь после msgbox напишите еще один код, как показано ниже,

Код:

Sub Sample()
On Error GoTo az
MsgBox 4 / 2
MsgBox 4 / 0
MsgBox 4 / 1
Done:
Exit Sub

пример ошибки vba 1.4

Теперь нам нужно определить пункт назначения az как то, что он должен делать, когда Excel находит ошибку в коде.

Код:

Sub Sample()
On Error GoTo az
MsgBox 4 / 2
MsgBox 4 / 0
MsgBox 4 / 1
Done:
Exit Sub
az:
MsgBox "This is an error " & Err.Description
End Sub

пример ошибки vba 1.5

Теперь, когда мы запускаем этот код, мы видим отображаемый результат.

пример ошибки vba 1.6

Это первый результат окна сообщения, и, поскольку мы знаем, что у нас есть ошибка в следующей строке нашего кода, давайте посмотрим результат, который даст Excel.

пример ошибки vba 1.7

err.description выше в коде помогает нам точно показать, какая ошибка произошла в нашем коде.

Пример #2

Мы научились обрабатывать ошибки в наших кодах. Давайте посмотрим на другой пример того, как обрабатывать ошибки. Рассмотрим следующий код в качестве нашего второго примера.

пример 2.1

У нас есть чем-то похожая ошибка из примера 1. Ошибка в строке d = i/b. Теперь мы будем обрабатывать эти ошибки, используя два описанных выше метода.

Напишите следующий код после открытия подфункции,

Код:

Sub Sample2()
On Error Resume Next bx
Dim i As Integer, b As Integer, c As Integer, d As Integer
i = 2
b = 0
c = i + b
MsgBox c
d = i / b
MsgBox d
End Sub

пример 2.2

Теперь, когда мы выполняем наш код, мы видим, что он игнорирует вторую строку и просто отображает значение для C.

пример 2.3

Вышеупомянутый обработчик ошибок был следующим для возобновления, и теперь мы будем использовать Go to, в котором мы скажем, что превосходим пункт назначения для перехода, когда он обнаруживает ошибку. Запишите следующий код,

Код:

Sub Sample2()
On Error GoTo bx
Dim i As Integer, b As Integer, c As Integer, d As Integer
i = 2
b = 0
c = i + b
MsgBox c
d = i / b
MsgBox d

пример 2.4

bx — это пункт назначения, который выдается при возникновении ошибки после того, как msgbox D запишет следующий код:

Код:

Sub Sample2()
On Error GoTo bx
Dim i As Integer, b As Integer, c As Integer, d As Integer
i = 2
b = 0
c = i + b
MsgBox c
d = i / b
MsgBox d
DOne:
Exit Sub

пример 2.5

Теперь нам нужно определить пункт назначения Bx, что он должен делать при обнаружении ошибки. Поэтому запишите следующий код:

Код:

Sub Sample2()
On Error GoTo bx
Dim i As Integer, b As Integer, c As Integer, d As Integer
i = 2
b = 0
c = i + b
MsgBox c
d = i / b
MsgBox d
DOne:
Exit Sub
bx:
MsgBox " This is another Error " & Err.Description
End Sub

пример 2.6

Теперь, когда мы выполняем код, мы видим, что Excel сначала дает нам значение для C.

пример 2.7

Теперь на другом этапе он даст нам подсказку, которую мы ему предоставили, когда он обнаружит ошибку.

пример 2.8

Вот как мы обрабатываем обычную среду выполнения ошибки в экселеОшибки в Excel распространены и часто возникают во время применения формул. Список из девяти наиболее распространенных ошибок Excel: #DIV/0, #N/A, #NAME?, #NULL!, #NUM!, #REF!, #VALUE!, #####, Circular Reference.читать далее VBA.

То, что нужно запомнить

Есть несколько вещей, которые нам нужно помнить об обработке ошибок:

  1. При следующем возобновлении ошибки игнорирует ошибку.
  2. On Error GoTo Отображает пункт назначения excel при обнаружении ошибки.
  3. Описание используется для отображения точной ошибки, возникшей у пользователя.

УЗНАТЬ БОЛЬШЕ >>

Post Views: 532

Обработка ошибок

Если вы когда-нибудь учились водить автомобиль, то наверняка вспомните, что при первой посадке на водительское сиденье все внимание было приковано к трем деталям: рулю, педалям и рычагу переключения передач. Происходящее вне автомобиля уходило на второй план, так как вначале нужно было стронуться с места. По мере практики навыки вождения улучшались и эти три детали постепенно уходили на задний план. Как ни странно, но руль и рычаг переключения передач всегда оказывались там, куда вы не смотря протягивали руки, а ноги сами находили педали. Теперь все внимание стало занимать происходящее на дороге. Иными словами, вы стали опытным водителем.

В программировании все абсолютно также. Начинающие программисты больше обращают внимание на первые попавшиеся на глаза операторы, функции и другие элементы языка, а сам алгоритм уходит на задний план. Если программа скомпилировалась без ошибок, то это уже большое счастье, хотя это еще не означает, что программа работает правильно. По мере практики мышление программиста меняется, он начинает обращать внимание на мелочи, на форматирование программы, использует более эффективные алгоритмы и в результате всего этого допускает меньше ошибок. Подводя итоги, можно сказать, что начинающий программист просто пишет программу, а опытный программист пытается найти оптимальный алгоритм и предусмотреть поведение программы в различных ситациях. Однако, от ошибок никто не застрахован, поэтому очень важно знать как быстро найти ошибку.

Типы ошибок

Существуют три типа ошибок в программе:

  • синтаксические — это ошибки в имени оператора или подпрограммы, отсутствие закрывающей или открывающей кавычек и т. д., то есть ошибки в синтаксисе языка. Как правило, компилятор предупредит о наличии ошибки, а программа не будет выполняться совсем;
  • логические — это ошибки в логике работы программы, которые можно выявить только по результатам работы программы. Как правило, компилятор не предупреждает о наличии ошибки, а программа будет выполняться, так как не содержит синтаксических ошибок. Такие ошибки достаточно трудно выявить;
  • ошибки времени выполнения — это ошибки, которые возникают во время работы программы. В одних случаях ошибки времени выполнения являются следствием логических ошибок, а в других случаях причиной являются внешние события, например, нехватка оперативной памяти, отсутствие прав для записи в файл и др.

Инструкция On Error

Ошибки времени выполнения можно перехватывать внутри подпрограммы. Для этого используется инструкция On Error, которая имеет три формата:

  • On Error GoTo <Метка> — при возникновении ошибки управление передается инструкции, помеченной меткой <Метка>. Метка должна быть допустимым идентификатором, к которому предъявляются такие же требования как и к переменным. Внутри подпрограммы метка указывается в самом начале помечаемой строки и после метки ставится двоеточие. В качестве примера создадим функцию для деления двух целых чисел. Внутри функции предусмотрим обработку ошибки деления на 0:
Function Деление(x As Integer, y As Integer) As Double
   On Error GoTo ПриОшибке
   Деление = x / y
   Exit Function
ПриОшибке:
   Деление = 0
End Function

Если при вызове функции во втором параметре передать значение 0, то управление будет передано в строку, помеченную с помощью метки ПриОшибке. Обратите внимание на то, что метка расположена после инструкции Exit Function. В этом случае код после инструкции Exit Function будет выполнен только в том случае, если возникнет ошибка;

  • On Error Resume Next — при возникновении ошибки управление передается следующей инструкции;
  • On Error GoTo 0 — отключает перехват ошибок.

Если внутри подпрограммы не предусмотрен перехват ошибки, то при возникновении ошибки работа программы прерывается и выводится стандартное окно с описанием и несколькими кнопками: Continue (продолжить), End (завершить выполнение программы), Debug (перейти в режим отладки) и Help (вывод справки).

Инструкция Resume

Инструкция Resume позволяет указать куда следует переходить после обработки ошибки. Инструкция имеет несколько форматов:

  • Resume [0] — управление передается инструкции, вызвавшей ошибку;
  • Resume Next — управление передается инструкции, следующей за инструкцией, вызвавшей ошибку;
  • Resume <Метка> — управление передается инструкции, помеченной меткой <Метка>.

Если инструкция Resume не указана, то выполняется только код внутри обработчика ошибки и производится выход из подпрограммы.

Получение информации об ошибке и генерация ошибки

Вся информация о последней ошибке доступна через объект Err. Объект содержит следующие свойства:

  • Number — код ошибки, например, код 11 для ошибки деления на 0. Если ошибки не произошло, то свойство содержит значение 0;
  • Description — описание ошибки, например, строка "Division by zero" для ошибки деления на 0. Пример вывода кода и описания ошибки:
Debug.Print Err.Number; Err.Description
  • Source — название текущего проекта;
  • HelpFile — путь к файлу справки;
  • HelpContext — идентификатор раздела в справочном файле;
  • LastDLLError — системный код ошибки при работе с DLL.

Объект Err содержит следующие методы:

  • Clear() — очищает всю информацию о последней ошибке. Этот метод следует вызвать после успешной обработки ошибки. Информация об ошибке автоматически очищается при выходе из подпрограммы и ряде других случаев;
  • Raise() — позволяет сгенерировать ошибку в программе. Формат метода:
Raise Number[, Source][, Description][, HelpFile][, HelpContext]

В параметре Number указывается код генерируемой ошибки (целое число от 0 до 65 535). Коды от 0 до 512 зарезервированы под системные ошибки, а остальные коды можно использовать под пользовательские ошибки. Чтобы сгенерировать ошибку с пользовательским кодом необходимо сложить код с константой vbObjectError. Остальные параметры являются необязательными и полностью аналогичны одноименным свойствам объекта Err. Пример генерации и обработки пользовательской ошибки:

Sub ГенерацияОшибки()
   On Error GoTo ПриОшибке
   Err.Raise vbObjectError + 513
   Exit Sub
ПриОшибке:
   Debug.Print Err.Number; Err.Description
   ' -2147220991 Automation error
End Sub

Способы поиска ошибок в программе

В предыдущих разделах мы научились обрабатывать ошибки времени выполнения. Однако, наибольшее количество времени программист затрачивает на другой тип ошибок — логические ошибки. В этом случае программа компилируется без ошибок, но результат выполнения программы не соответствует ожидаемому результату. Ситуация еще более осложняется, когда неверный результат проявляется лишь периодически, а не постоянно. Инсценировать такую же ситуацию, чтобы получить этот же неверный результат, бывает крайне сложно и занимает очень много времени. В этом разделе мы рассмотрим лишь «дедовские» (но по прежнему актуальные) способы поиска ошибок, а современные способы отладки приложений, доступные в VBA, изучим в следующем разделе.

Первое, на что следует обратить внимание, — на объявления переменных. Например, рассмотрим простой пример:

Как вы думаете, какое значение отобразится в окне Immediate после выполнения этого кода? Думаете, что число 10? Не факт! Вот тут-то и кроется проблема не видная на первый взгляд. В первой инструкции присваивается значение переменной x, имя которой набрано на английской раскладке клавиатуры, а вот во второй инструкции выводится значение переменной x, имя которой набрано на русской раскладке клавиатуры. В результате значение присваивается одной переменной, а выводится значение другой переменной. Такие ситуации очень часто встречаются в программах на языке VBA, так как объявлять переменную не обязательно. Чтобы избежать такой ситуации следует обязательно объявлять переменные явным образом. Контроль за соблюдением этого правила можно возложить на компилятор, добавив в начале модуля следующую инструкцию:

При наличии инструкции компилятор производит проверку объявления всех переменных. Если переменная не была объявлена явным образом, то компилятор выведет сообщение об ошибке и выполнение программы будет остановлено. Таким образом, код должен выглядеть следующим образом:

Option Explicit
...
Dim x As Integer
x = 10
Debug.Print x ' 10

Далее следует обратить внимание на форматирование кода. Начинающие программисты обычно не обращают на это никакого внимания, считая этот процесс лишним. А на самом деле зря! Компилятору абсолютно все равно, разместите вы все инструкции на одной строке или выполните форматирование кода. Однако, при поиске ошибок форматирование кода позволит найти ошибку гораздо быстрее.

Перед всеми инструкциями внутри блока должно быть расположено одинаковое количество пробелов. Обычно используют три или четыре пробела. От применения символов табуляции лучше отказаться. Если все же используете, то не следует в одном файле совмещать и пробелы и табуляцию. Для вложенных блоков количество пробелов умножают на уровень вложенности. Если для блока первого уровня вложенности использовалось три пробела, то для блока второго уровня вложенности должно использоваться шесть пробелов, для третьего уровня — девять пробелов и т. д. Пример форматирования вложенных блоков приведен в листинге 11.1.

Dim Массив As Variant, i As Integer, j As Integer
Массив = Array(Array(0, 1), Array(2, 3), Array(4, 5))
For i = 0 To 2
   For j = 0 To 1
      Debug.Print Массив(i)(j)
   Next
Next

Длина одной строки не должна содержать более 80 символов. Если количество символов больше, то следует выполнить переход на новую строку. При этом продолжение смещается относительно основной инструкции на величину отступа или выравнивается по какому-либо элементу. Иначе приходится пользоваться горизонтальной полосой прокрутки, а это очень неудобно при поиске ошибок.

Если программа слишком большая, то следует задуматься о разделении программы на отдельные подпрограммы или классы, которые выполняют логически законченные действия. Помните, что отлаживать отдельную подпрограмму гораздо легче, чем «спагетти»-код. Причем прежде чем вставить подпрограмму (или класс) в основную программу ее следует протестировать в отдельном проекте, передавая подпрограмме различные значения и проверяя результат ее выполнения.

Обратите внимание на то, что форматирование кода должно выполняться при написании кода, а не во время поиска ошибок. Этим вы сократите время поиска ошибки и скорее всего заметите ошибку еще на этапе написания. Если все же ошибка возникла, то вначале следует инсценировать ситуацию, при которой ошибка проявляется. После этого можно начать поиск ошибки.

Причиной периодических ошибок чаще всего являются внешние данные. Например, если числа получаются от пользователя, а затем производится деление чисел, то вполне возможна ситуация, при которой пользователь введет число 0. Деление на ноль приведет к ошибке. Следовательно, все данные, которые поступают от пользователей, должны проверяться на соответствие допустимым значениям. Если данные не соответствуют, то нужно вывести сообщение об ошибке, а затем повторно запросить новое число или прервать выполнение всей программы. Кроме того, нужно обработать возможность того, что пользователь может ввести вовсе не число, а строку.

Метод Print() объекта Debug удобно использовать для вывода промежуточных значений. В этом случае значения переменных вначале выводятся в самом начале программы и производится проверка соответствия значений. Если значения соответствуют, то инструкция с методом Print() перемещается на следующую строку программы и опять производится проверка и т. д. Если значения не совпали, то ошибка возникает в инструкции, расположенной перед инструкцией с методом Print(). Если это пользовательская подпрограмма, то проверку значений производят внутри подпрограммы, каждый раз перемещая инструкцию с выводом значений. На одном из этих многочисленных этапов ошибка обычно обнаруживается. В больших программах можно логически догадаться о примерном расположении инструкции с ошибкой и начать поиск ошибки оттуда, а не с самого начала программы.

Инструкции для вывода промежуточных значений можно расставить уже при написании программы, не дожидаясь возникновения ошибки. В этом случае в начале программы объявляется константа с помощью инструкции #Const, а внутри программы производится проверка значения константы:

Проверить значение константы позволяет следующая конструкция:

#If MY_DEBUG Then
   ' Здесь размещаем инструкции вывода значений
#End If

Таким образом, меняя значение константы MY_DEBUG с 1 на 0, можно отлючать вывод всех промежуточных значений.

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

Прежде чем начать отладку необходимо пометить строки внутри программы с помощью точек останова. Для добавления точки останова делаем строку активной, а затем из меню Debug выбираем пункт Toggle Breakpoint. Слева от строки появится кружок, обозначающий точку останова. Добавить точку останова можно еще быстрее. Для этого достаточно щелкнуть слева от строки левой кнопкой мыши. Повторный щелчок позволяет удалить точку останова. Кроме того, для добавления или удаления точки отстанова можно воспользоваться клавишей <F9>. Чтобы удалить все точки останова следует из меню View выбрать пункт Clear All Breakpoints.

Когда точки останова расставлены можно начать отладку. Для этого запускаем программу на выполнение обычным способом. При достижении точки останова выполнение программы прерывается и отладчик ожидает дальнейших действий программиста. Инструкция, которая будет выполняться на следующем шаге, помечается желтой стрелкой слева от строки.

В режиме прерывания можно посмотреть значения различных переменных в окне Locals. Если окно не отображается, то отобразить его можно выбрав в меню View пункт Locals Window. Посмотреть значение переменной можно также если навести указатель мыши на переменную. Значение переменной отобразится во всплывающей подсказке.

При отладке можно контролировать значения отдельных переменных, а не всех сразу. Для этого следует выделить название переменной и из меню Debug выбрать пункт Add Watch. Можно также выделить название переменной и из контектного меню выбрать пункт Add Watch. В открывшемся окне устанавливаем флажок Watch Expression и нажимаем кнопку OK. Значение переменной будет отображаться в окне Watches. Чтобы отобразить окно Watches из меню View выбираем пункт Watch Window. Чтобы отменить отслеживание нужно выделить строку в окне Watches и нажать клавишу <Delete>.

Для пошагового выполнения программы предназначены следующие пункты в меню Debug или соответствующие кнопки на панели инструментов Debug (View | Toolbars | Debug):

  • Step Into (клавиша <F8>) — выполняет переход к следующей инструкции;
  • Step Over — выполняет одну инструкцию. Если в этой инструкции производится вызов подпрограммы, то подпрограмма выполняется за один шаг и отладчик переходит в режим ожидания после выхода из подпрограммы;
  • Step Out — при заходе в подпрограмму этот пункт позволяет выполнить подпрограмму за один шаг и выйти из нее. Отладчик переходит в режим прерывания после выхода из подпрограммы;
  • Run To Cursor — выполняет переход к инструкции, в которой расположен курсор.

Если необходимо посмотреть последовательность вызова подпрограмм, то следует открыть окно Call Stack, выбрав в меню View пункт Call Stack.

Подача звукового сигнала

При возникновении ошибки или при неправильном вводе данных имеет смысл привлечь внимание пользователя звуковым сигналом. Сгенерировать звуковой сигнал позволяет инструкция Beep. Пример:

Dim Результат
Beep
Результат = InputBox("Необходимо ввести значение")

Visual Basic for Applications (VBA)
Статьи по Visual Basic for Applications (VBA)

 

LightZ

Пользователь

Сообщений: 1748
Регистрация: 22.12.2012

Всем привет.  
Подскажите, пожалуйста, как обойти ошибку #Н/Д с помощью VBA?  
Использую VLookup.  
Код ниже:  
Dim s, i As Integer  
For i = 1 To 5  
s = Application.WorksheetFunction.VLookup(Range(«A» & i), Range(«C» & i), 1, 0)  
Range(«B» & i) = s  
Next  

  Всем заранее спасибо!

Киса, я хочу Вас спросить, как художник — художника: Вы рисовать умеете?

 

ну, приложите небольшой файл-пример (нам так легче будет для вас код писать и тестировать)  

  P.S. А так, что в VBA, что на обычно листе #Н/Д обходится EНД (ISNA). Почитайте в Справке

 

LightZ

Пользователь

Сообщений: 1748
Регистрация: 22.12.2012

{quote}{login=Ластик}{date=21.01.2012 03:59}{thema=}{post}ну, приложите небольшой файл-пример (нам так легче будет для вас код писать и тестировать)  

  P.S. А так, что в VBA, что на обычно листе #Н/Д обходится EНД (ISNA). Почитайте в Справке{/post}{/quote}  
ну в экселе можно исправить с помощью ЕСЛИ и ЕНД, а как это сделать в VBA — не знаю.  
файл прилагаю.

Киса, я хочу Вас спросить, как художник — художника: Вы рисовать умеете?

 

LightZ

Пользователь

Сообщений: 1748
Регистрация: 22.12.2012

к примеру если изменить код, следующим образом:  
s = Application.WorksheetFunction.VLookup(Range(«A:A»), Range(«C:C»), 1, 0)  
Range(«B:B») = s  

  на те строки, которые не совпадают с помощью ВПР — получаться ошибки #Н/Д.  
как с помощью VBA заменить #Н/Д на «0» ?  

  И как обойти ошибку VBA в вышеуказанном примере?

Киса, я хочу Вас спросить, как художник — художника: Вы рисовать умеете?

 

сейчас посмотрим, но у меня такое чувство, что вы не знаете, как работает ВПР на листе. В Справке написано:  

  ВПР(искомое_значение, таблица, номер_столбца, [интервальный_просмотр])

  т.е. (что искать, в каком диапазоне ячеек искать, номер столбца, интервальный просмотр)  

  А вы в коде какую-то ерунду пишите

 

Hugo

Пользователь

Сообщений: 23379
Регистрация: 22.12.2012

Как-то всё глупо… Но по существу вопроса — в этом примере можно так:  

    Sub f()  
Dim s, i As Integer  
On Error Resume Next  
For i = 1 To 5  
s = Application.WorksheetFunction.VLookup(Range(«A» & i), Range(«C» & i), 1, 0)  
Range(«B» & i) = s  
s = Empty  
Next  
End Sub

 

LightZ

Пользователь

Сообщений: 1748
Регистрация: 22.12.2012

{quote}{login=Hugo}{date=21.01.2012 04:31}{thema=}{post}Как-то всё глупо… Но по существу вопроса — в этом примере можно так:  

    Sub f()  
Dim s, i As Integer  
On Error Resume Next  
For i = 1 To 5  
s = Application.WorksheetFunction.VLookup(Range(«A» & i), Range(«C» & i), 1, 0)  
Range(«B» & i) = s  
s = Empty  
Next  
End Sub{/post}{/quote}  

  Спасибо, сам не додумался, писал просто On Error Resume Next, но ничего не получалось.  

  Почему глупо? я выложил просто минимальный пример ВПР, только необходимо заменить #Н/Д на 0 с помощью VBA  
Формулой бы это выглядело так — =ЕСЛИ(ЕНД(ВПР(A:A;C:C;1;0));0;(ВПР(A:A;C:C;1;0)))

Киса, я хочу Вас спросить, как художник — художника: Вы рисовать умеете?

 

LightZ

Пользователь

Сообщений: 1748
Регистрация: 22.12.2012

{quote}{login=Ластик}{date=21.01.2012 04:29}{thema=}{post}сейчас посмотрим, но у меня такое чувство, что вы не знаете, как работает ВПР на листе. В Справке написано:  

  ВПР(искомое_значение, таблица, номер_столбца, [интервальный_просмотр])

  т.е. (что искать, в каком диапазоне ячеек искать, номер столбца, интервальный просмотр)  

  А вы в коде какую-то ерунду пишите{/post}{/quote}  
хех, почему ж ерунду? :)  
в примере диапазон А:А это искомое значение, С:С это таблица, 1 это № столбца  

  Что же тут не понятно ?:)

Киса, я хочу Вас спросить, как художник — художника: Вы рисовать умеете?

 

Hugo

Пользователь

Сообщений: 23379
Регистрация: 22.12.2012

Что за А:А, С:С?  
ОДНО искомое значение ищем в ДИАПАЗОНЕ!  
А у Вас в коде одно значение ищется в одном значении, а в примере формулы диапазон в диапазоне… Ерунда!

 

Юрий М

Модератор

Сообщений: 60809
Регистрация: 14.09.2012

Контакты см. в профиле

LightZ, для начала загляните сюда: 

http://www.planetaexcel.ru/tip.php?aid=26

поймёте свою ошибку. Также полезно почитать встроенную справку по этой функции. Особое внимание обратите на искомое значение.

 

нарисовал для вас пример, см. файл

 

Igor67

Пользователь

Сообщений: 3733
Регистрация: 21.12.2012

Читал, читал ну не понимаю на какой Х? ВПР?  
сравниваем по строкам, ну так почему не через простое равно?  
Sub f()  
Dim i As long  
On Error Resume Next  
For i = 1 To 5  
if cells(i,1)=cells(i,3) then    
cells(i,2)=cells(i,1)  
else: cells(i,1)=0  
Next  
End Sub

 

KuklP

Пользователь

Сообщений: 14868
Регистрация: 21.12.2012

E-mail и реквизиты в профиле.

Зря накинулись на человека. Каждый имеет право на ошибку. Пусть бы помучался. Зато потом бы ВПР на всю жизнь запомнил:-) А так быстро забудет.

Я сам — дурнее всякого примера! …

 

Hugo

Пользователь

Сообщений: 23379
Регистрация: 22.12.2012

И вообще — в коде использовать формулы листа как-то неправильно (на мой взгляд).  
Хотя я тоже иногда использую — но это скорее из лени.  
В данном случае — на каждом шаге цикла формула будет прогонять через себя весь диапазон и искать нужное значение. Пусть это быстро встроенным механизмом Экселя, но думаю быстрее было бы сперва взять диапазон в массив, потом загнать его в словарь (один перебор массива), затем искать в словаре одним проходом по массиву из столбца.  
Думаю, что цикл поиска в словаре быстрее поиска ВПРом по диапазону.

 

а я вообще не понял, что человек хочет сделать. Спрашивает про ВПР, ответил про ВПР ))  

  По-моему, человеку нужно было сделать так  
1) создать пример в файле  
2) создать тему с вопросом «ааа, ребятаааа, помогите в VBA …. (дальше идут вариации) …. сравнить 2 столбца…. подтянуть данные из одного столбца в другой… и т.д.»  

  а мы бы уже что-нибудь придумали, а то «НД в ВПР», а что такое ВПР сам не знает

 

Hugo

Пользователь

Сообщений: 23379
Регистрация: 22.12.2012

А если делать, как в примере Ластика, так там в каждой ячейке по два ВПР() отрабатывают… не всегда, но в большинстве случаев.  
Или вторая формула берёт уже вычисленное значение? Кто б потестил… :)

 

Юрий М

Модератор

Сообщений: 60809
Регистрация: 14.09.2012

Контакты см. в профиле

{quote}{login=KukLP}{date=21.01.2012 04:56}{thema=}{post}Зря накинулись на человека. {/post}{/quote}Серж, никто не набрасывался — ему говорят про его ошибку, а в ответ снова неверное представление формулы и «Что же тут не понятно ?:)»    
Вот и спрашивается: человеку нужна помощь или зашёл нас вразумить?

 

Игорь, формулистов на тебя не хватает ))

 

Hugo

Пользователь

Сообщений: 23379
Регистрация: 22.12.2012

А кстати меня это давно интересует — два раза ВПР отрабатывает или один? Хотя тут это офф…

 

KuklP

Пользователь

Сообщений: 14868
Регистрация: 21.12.2012

E-mail и реквизиты в профиле.

{quote}{login=Юрий М}{date=21.01.2012 05:04}{thema=Re: }{post}{quote}{login=KukLP}{date=21.01.2012 04:56}{thema=}{post}Зря накинулись на человека. {/post}{/quote}Серж, никто не набрасывался — ему говорят про его ошибку, а в ответ снова неверное представление формулы и «Что же тут не понятно ?:)»    
Вот и спрашивается: человеку нужна помощь или зашёл нас вразумить?{/post}{/quote}  
Я ж и говорю, пусть сам прочувствует:-) Вон у Володи в подписи: «И то, что понято с трудом, то мне дороже..»

Я сам — дурнее всякого примера! …

 

{quote}{login=Hugo}{date=21.01.2012 05:07}{thema=}{post}А кстати меня это давно интересует — два раза ВПР отрабатывает или один? Хотя тут это офф…{/post}{/quote}  

  я думаю 2 раза, потому как нельзя проверить будет ли НД не рассчитав первый ВПР.  
Просто если таблица до 1000 строк, это не принципиально. В каждом рабочем файле (в моём например), всегда куча формул было разных. Это же не чистая база данных, там расчёты, обычно, идут.

 

LightZ

Пользователь

Сообщений: 1748
Регистрация: 22.12.2012

Всем спасибо!  
блин, да знаю я как пользоваться ВПР :)  
вот только-что накидал — во вложении пример ВПР в VBA

Киса, я хочу Вас спросить, как художник — художника: Вы рисовать умеете?

 

Hugo

Пользователь

Сообщений: 23379
Регистрация: 22.12.2012

В 10 раз быстрее:  

  Sub tt()  
   Dim tm!: tm = Timer  
   Dim a(), b(), i&, ii&  
   b = Sheets(2).UsedRange.Value  
   With CreateObject(«Scripting.Dictionary»)  
       For i = 1 To UBound(b)  
           .Item(b(i, 1)) = i  
       Next  
       a = Sheets(1).UsedRange.Columns(1).Value  
       ReDim c(1 To UBound(a), 1 To 3)  
       For i = 1 To UBound(a)  
           If .exists(a(i, 1)) Then  
               ii = .Item(a(i, 1))  
               c(i, 1) = b(ii, 2)  
               c(i, 2) = b(ii, 3)  
               c(i, 3) = b(ii, 4)  
           Else  
               c(i, 1) = «Данных в базе нет»  
               c(i, 2) = «Данных в базе нет»  
               c(i, 3) = «Данных в базе нет»  

             End If  
       Next  

     End With  
   Sheets(1).[B1:D1].Resize(i — 1) = c
   Debug.Print Timer — tm  
End Sub  

      Сравните время:  
0,15625    
0,140625    
0,15625    
0,015625    
0,03125    
0,015625

 

LightZ

Пользователь

Сообщений: 1748
Регистрация: 22.12.2012

Спасибо Hugо, так я ещё не умею.  
буду разбирать код ;)

Киса, я хочу Вас спросить, как художник — художника: Вы рисовать умеете?

 

ну, и я свой вариант на массивах

 

Hugo

Пользователь

Сообщений: 23379
Регистрация: 22.12.2012

Алгоритм выше я уже описАл, он не ноухау — диапазон в массив, его в словарь, второй в массив, проверяем по словарю.  
Плюс создал массив для результатов — хотя можно было сделать массив а пошире и складывать в него, его же назад и выгрузить. Но тогда затрутся/перепишутся данные первого столбца, что лишняя работа.

 

LightZ

Пользователь

Сообщений: 1748
Регистрация: 22.12.2012

{quote}{login=Ластик}{date=21.01.2012 06:19}{thema=}{post}ну, и я свой вариант на массивах{/post}{/quote}  
спасибо за комментарии в коде

Киса, я хочу Вас спросить, как художник — художника: Вы рисовать умеете?

 

Сравнил по скорости мой код с Hugo, что-то у меня выходил либо 0,0078125 либо 0 и там и там )  

  P.S. На больших объёмах Словарь выиграет, на небольших незаметно.

 

Hugo

Пользователь

Сообщений: 23379
Регистрация: 22.12.2012

Ну на таком объёме тестить конечно несерьёзно — я вообще удивлён, что у меня такая разница получилась. Там может основная потеря времени на «инициализацию» формулы уходит, чего в моём коде нет.  
Точнее тысяч на 40 строк проверять.

 

LightZ

Пользователь

Сообщений: 1748
Регистрация: 22.12.2012

#30

21.01.2012 18:38:28

{quote}{login=Ластик}{date=21.01.2012 06:30}{thema=}{post}Сравнил по скорости мой код с Hugo, что-то у меня выходил либо 0,0078125 либо 0 и там и там )  

  P.S. На больших объёмах Словарь выиграет, на небольших незаметно.{/post}{/quote}  
добавил и свой вариант, для сравнения скорости, на данный момент разницы практически нет.  
но наверное нужно потестить на больших массивах…

Прикрепленные файлы

  • post_299397.xlsm (24.52 КБ)

Киса, я хочу Вас спросить, как художник — художника: Вы рисовать умеете?

I am using a Vlookup in a VBA function as follows:

result = Application.WorksheetFunction.VLookup(target_arg, table_arg, 2, False)

When an entry exists in the lookup table (table_arg), then all is well.

If an entry does not exist in the table, then the cell has a #Value! result. I’ve tried to do some error trapping, but once the Vlookup doesn’t find a value, the function exits and none of my code after the above statement is executed.

I would like to trap this case so that I can do additional processing before I exit the function.

“Constant effort and frequent mistakes are the stepping stones to genius” – Elbert Hubbard

Contents

  • 1 A Quick Guide to the VBA VLookup
  • 2 Introduction
  • 3 A Simple VBA VLookup example
  • 4 The Parameters
    • 4.1 Parameter 1: lookup_value
      • 4.1.1 Using VLookup on a Date Type
    • 4.2 Parameter 2: table_array
    • 4.3 Parameter 3: col_index-num
    • 4.4 Parameter 4: range_lookup
  • 5 Dealing with Errors
    • 5.1 Using WorksheetFunction
    • 5.2 Using Application
    • 5.3 VLookup Error Types
    • 5.4 Errors and Causes
  • 6 VBA VLookup Speed
  • 7 What’s Next?

A Quick Guide to the VBA VLookup

Parameters Type
Lookup value The value you are searching for
Table array The range you are searching through
Column index The column number of the value to return.
Range look up Optional — set to False for exact match only.

Introduction

The VLookup function can be a useful Excel function. Even though it is straightforward to use can often be confusing when used in VBA. In this post, I am going to show how anyone can easily use the VLookup function. I’ll also cover the pitfalls and how to avoid them. Of course, no post would be complete without a ton of examples that you can try for yourself.

If you are not familiar with VLookup in Excel then this page provides a great introduction.

Notes: I use the Underscore character(_) in the code examples. In VBA this allows you to split a line over multiple lines e.g.

' One line
sResult = Application.VLookup("Apricot", Sheet1.Range("A10:B10000"), 1)
' Split up with underscore
sResult = Application.VLookup( _
    "Apricot", Sheet1.Range("A10:B10000"), 1)

A Simple VBA VLookup example

Note: The variable shData in the examples refers to the worksheet by the code name. You can replace this with the code name of the worksheet you are using.

Take a look at the following data:

VBA VLookup

Use this code to generate this data on any worksheet:

' Use this sub to generate the data
' https://excelmacromastery.com
Sub GenerateData()
    
    ' Change the sheet name as required
    With ThisWorkbook.Worksheets("Sheet1")
    
        .Range("A1").CurrentRegion.ClearContents
        .Range("A1:A7").Value = WorksheetFunction.Transpose(Array("Fruit", "Apple", "Apricot", "Orange", "Peach", "Pair", "Plum"))
        .Range("B1:B7").Value = WorksheetFunction.Transpose(Array("Price", 1.56, 2.33, 1.45, 2.28, 1.67, 1.22))

    End With
    
End Sub

The code below will return the price for the Pear i.e. 1.45

' https://excelmacromastery.com/
Sub SimpleVLookup()
    
    Dim sRes As String
    sRes = Application.VLookup("Pear",shData.Range("A2:B7"),2)
    
    ' This will print 1.67 to the Immediate Window(Ctrl + G)
    Debug.Print sRes
    
End Sub

The code looks for the text Pear in the range A2:B7. When it finds the text it returns a value from the same row as the text. The value in determined by the column number argument. We used 2 in this example.

VBA Lookup

 
VBA Lookup

 
Let’s look at some more examples and results

' Returns 1.45
sRes = Application.VLookup("Orange",shData.Range("A2:B7"),2)

' Returns 1.56
sRes = Application.VLookup("Apple",shData.Range("A2:B7"),2)

' Returns 1.22
sRes = Application.VLookup("Plum",shData.Range("A2:B7"),2)

' Returns Orange as column is 1
sRes = Application.VLookup("Orange",shData.Range("A2:B7"),1)

' Returns Apple as column is 1
sRes = Application.VLookup("Apple",shData.Range("A2:B7"),1)

' Returns Plum as column is 1
sRes = Application.VLookup("Plum",shData.Range("A2:B7"),1)

The Parameters

In this section we will look at the four parameters. These are

  1. lookup_value – The value to look up. It must be in the first column of the range.
  2. table_array – This is the range to search. This can also be a VBA array although it very slow using this.
  3. col_index_num – This contains the column number of the return value. Starts at column number one.
  4. range_lookup(optional) – Use True(default) to find closest match. Use False to find exact match. Using True assumes that the first columnis sorted alphabetically or numerically.

We will look at these parameters individually starting with the lookup_value parameter.

Parameter 1: lookup_value

This is the value that you are looking up. It must be in the first column of the Range. If you are using the range C4:X10 then the lookup value must be in column C. If you are using the range Z1:AB5 then the lookup value must be in column Z.

The type of value you search for will normally be a string as this example shows

' https://excelmacromastery.com/
Sub StringVLookup()
    
    Dim sFruit As String
    sFruit = "Plum"
    
    Dim sRes As Variant
    sRes = Application.VLookup( _
                       sFruit, shData.Range("A2:B7"), 2, False)
    
End Sub

 
We can also search for a number but you have to be careful here:

  1. If the number is stored as text then the search value must be a string.
  2. If the number is stored as a number then the search value must be a number.

 
For example in this data we have the lookup column stored as numbers

VBA Lookup

In this case, the lookup value must be a Long or you will get an error message.

' https://excelmacromastery.com/
Sub NumberVLookup()
    
    Dim num As Long
    num = 7
    
    Dim sRes As Variant
    sRes = Application.VLookup( _
                  num, shData.Range("F2:G7"), 2, True)
    
    Debug.Print sRes
    
End Sub

 
You can also use the Double data type if you are looking up a decimal value. As in the case of an integer it must be stored as a number if you want to use Double.

Using VLookup on a Date Type

Using a Date type is a tricky business. VBA has a Date data type but the worksheet does not.

So the date type needs to be converted to a Long as the following examples show

theDate = CLng(#1/14/2017#)

theDate = CLng(CDate("1/14/2017"))

theDate = CLng(shData.Range("H10"))

 
You can then use it as normal in the VLookup function when the search column contains dates

' https://excelmacromastery.com/
Sub DateVLookup()
    
    Dim theDate As Long
    theDate = CLng(#1/14/2017#)
    
    Dim sRes As Variant
    sRes = Application.VLookup( _
                 theDate, shData.Range("I2:J7"), 2, False)
    
    Debug.Print sRes
    
End Sub

Parameter 2: table_array

This parameter refers to the range of the data we are looking up. You will normally use a range for this as we have seen in the examples so far.

If you are using a worksheet table you can use the range of the table.

' https://excelmacromastery.com/
Sub SimpleVLookupTable()
    
    Dim sRes As Variant
    
    ' Get the table
    Dim table As ListObject
    Set table = shData.ListObjects("Table1")
    
    ' Use the table for the table_array parameter
    sRes = Application.VLookup( _
                  "Plum", table.Range, 2, False)
    
    Debug.Print sRes
    
End Sub

You can also use a VBA array with VLookup but this tends to be very slow.

Parameter 3: col_index-num

This parameter refers to the column which contains the value you want to return. Column 1 is the leftmost column of the table_array.

If the column number is greater than the number of columns in the range you will get an error. See The VLookup Error Types section below.

VBA Lookup

© BigStockPhoto.com

Parameter 4: range_lookup

This is an optional parameter. If it is not used then it takes True as the default value.

False means that an exact match must be found.
True means that an approximate match will be returned. The first column must be ordered numerically or alphabetically for this to work correctly.

Let’s look at the sample data again

VBA VLookup

The following code shows some examples of how this parameter works:

' https://excelmacromastery.com/
Sub SimpleVLookup()

    Dim rg As Range
    Set rg = shData.Range("A2:B7")
    
    Dim sRes As Variant
    
    ' Stops at Orange - the last item before a P item
    sRes = Application.VLookup("P", rg, 2, True)
    
    ' Stops at Orange - the last item before a Pea item
    sRes = Application.VLookup("Pea", rg, 2, True)
    
    ' Stops at Peach - the last item before a Pead item
    sRes = Application.VLookup("Pead", rg, 2, True)
    
    ' Error - no exact match found
    sRes = Application.VLookup("Pea", rg, 2, False)

    
End Sub

Dealing with Errors

VBA VLookup Errors

© BigStockPhoto.com

 
We can use VLookup in two ways in VBA. With Application or with WorksheetFunction

Application.WorksheetFunction.VLookup

Application.VLookup

 
The difference between them is how we handle errors. Let’s look at each of these in turn.

Using WorksheetFunction

Using WorksheetFunction.VLookup requires us to use On Error to trap the error. We can then check the error number Err.Number to see if the value is valid.

' https://excelmacromastery.com/
Sub UseWorksheetFunction()
    
    Dim sRes As Variant
    
    ' Turn on error trapping
    On Error Resume Next
    Err.Clear
    
    sRes = Application.WorksheetFunction.VLookup _
                ("Plum", shData.Range("A2:B7"), 2, False)
    
    ' Check if value found
    If Err.Number = 0 Then
        Debug.Print "Found item. The value is " & sRes
    Else
        Debug.Print "Could not find value: " & "Plum"
    End If
    
End Sub

Using Application

Using Application.VLookup we can simply check the return value to see if there was an error

' https://excelmacromastery.com/
Sub UsingApplication()
    
    Dim sRes As Variant
    
    sRes = Application.VLookup _
                ("Plum", shData.Range("A2:B7"), 2, False)
    
    ' Check if value found
    If IsError(sRes) = False Then
        Debug.Print "Found item. The value is " & sRes
    Else
        Debug.Print "Could not find value: " & "Plum"
    End If
    
End Sub

VLookup Error Types

The following table shows a list of the Excel cell error numbers and what they mean. These are the error numbers we get when we use Application.VLookup. This is taken from this MSDN Page

Constant Error number Cell error value
xlErrDiv0 2007 #DIV/0
xlErrNA 2042 #N/A
xlErrName 2029 #NAME?
xlErrNull 2000 #NULL!
xlErrNum 2036 #NUM!
xlErrRef 2023 #REF!
xlErrValue 2015 #VALUE!

Errors and Causes

The following table shows some common errors you may encounter with VLookup. If you’re having trouble with a particular VLookup error then it is a good idea to try it in Excel first.

Error Cell Possible causes
Error 2015 #VALUE! The column number is less than one.
Error 2015 #VALUE! You used a string instead of a range for the table_array parameter.
Error 2023 #REF! The column number is greater than the number of columns.
Error 2042 #N/A The value is not found. See possible causes below.

If you cannot find the value then check the following:

  1. Ensure the Table/Range is correct.
  2. Ensure the Table/Range does not include the header(VBA will think list is unsorted).
  3. Ensure the Table/Range is using the correct worksheet.
  4. If searching for a number use a long or double data type in the lookup_value parameter. See lookup_value section above
  5. If searching for a number stored as text use a string data type in the lookup_value parameter.
  6. If searching for a date convert it to a long(see Date Type above) in the lookup_value parameter.

If you are getting the wrong value then check the following:

  1. If the range_lookup parameter is True or not used, then ensure the first column is sorted alphabetically or numerically (see range_lookup above)

VBA VLookup Speed

Sometimes you may need to perform a large number of lookups. In these cases, VLookup could be too slow. The VBA Dictionary is faster when dealing with a large number of lookups. The following code shows an example of using the Dictionary.

' https://excelmacromastery.com/
Sub UseDictionary()

    ' Get the range of values
    Dim rg As Range
    Set rg = shData.Range("M1:N20000")
    
    ' Create the dictionary
    Dim dict As Object
    Set dict = CreateObject("Scripting.Dictionary")
    
    ' Fill the dictionary
    Dim cell As Range
    For Each cell In rg
        dict(cell.Value) = cell.Offset(0, 1).Value
    Next
    
    ' Perform the Lookups
    For Each cell In rg
        Debug.Print dict(cell.Value)
    Next

End Sub

What’s Next?

Free VBA Tutorial If you are new to VBA or you want to sharpen your existing VBA skills then why not try out the The Ultimate VBA Tutorial.

Related Training: Get full access to the Excel VBA training webinars and all the tutorials.

(NOTE: Planning to build or manage a VBA Application? Learn how to build 10 Excel VBA applications from scratch.)

Возможно, вам также будет интересно:

  • Vb net webbrowser ошибка сценария
  • Vb net msgbox ошибка
  • Vat gov by ошибка при попытке подключения к tls серверу vat gov by
  • Vat gov by внутренняя ошибка библиотеки
  • Vat 2000 коды ошибок

  • Понравилась статья? Поделить с друзьями:
    0 0 голоса
    Рейтинг статьи
    Подписаться
    Уведомить о
    guest

    0 комментариев
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии