Необъяснимая ошибка несоответствия типов примерно каждые 10 000 итераций в Excel VBA

У меня есть макрос VBA, который использует Microsoft MapPoint для расчета расстояния между двумя местоположениями для каждой записи в моей электронной таблице. Мне нужно обработать около 120 000 записей. Программа работает плавно около 10 000 итераций, а затем возвращает ошибку несоответствия типов, где я определяю местоположения MapPoint в своем обработчике ошибок. В этот момент я выбираю «Отладка», а затем возобновляю выполнение без редактирования какого-либо кода, и он будет успешно работать еще около 10 000 записей, прежде чем то же самое произойдет снова.

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

Для справки:
 – столбец M содержит местоположения в форме "X County, ST"
 – столбец AN содержит отдельное местоположение в виде почтового индекса
 – столбец G содержит те же данные о местоположении, что и AN, но в форме " Округ Х, ST »

Sub distance_from_res()
Dim oApp As MapPoint.Application
Dim k As Long  
Dim count As Long 
Dim errors As Long 

k = 0
count = Sheets("i1_20041").Range("A2", Sheets("i1_20041").Range("A2").End(xlDown)).count
errors = 0

  Set oApp = CreateObject("MapPoint.Application.NA.11")
  oApp.Visible = False
  Set objMap = oApp.NewMap
  Dim objRes As MapPoint.Location
  Dim objFish As MapPoint.Location

'Error executes code at 'LocError' and then returns to point of error.
  On Error GoTo LocError
  Do While k < count
    If Sheets("i1_20041").Range("M2").Offset(k, 0) <> "" Then
        'Sets MapPoint locations as [County],[State] from Excel sheet columns "INT_CNTY_ST" and "ZIP".
          Set objRes = objMap.FindResults(Sheets("i1_20041").Range("AN2").Offset(k, 0)).Item(1)
          Set objFish = objMap.FindResults(Sheets("i1_20041").Range("M2").Offset(k, 0)).Item(1)
        'Calculates distance between two locations and prints it in appropriate cell in Column AO.
          Sheets("i1_20041").Range("AO2").Offset(k, 0) = objRes.DistanceTo(objFish)
    Else
        errors = errors + 1
    End If
      k = k + 1
  Loop
 'Displays appropriate message at termination of program.
  If errors = 0 Then
    MsgBox ("All distance calculations were successful!")
  Else
    MsgBox ("Complete! Distance could not be calculated for " & errors & " of " & count & " records.")
  End If

Exit Sub

LocError:
    If Sheets("i1_20041").Range("G2").Offset(k, 0) = "" Then
        errors = errors + 1
    Else
        'THIS IS WHERE THE ERROR OCCURS!
          Set objRes = objMap.FindResults(Sheets("i1_20041").Range("G2").Offset(k, 0)).Item(1)
          Set objFish = objMap.FindResults(Sheets("i1_20041").Range("M2").Offset(k, 0)).Item(1)
        'Calculates distance between two locations and prints it in appropriate cell in Column AO.
          Sheets("i1_20041").Range("AO2").Offset(k, 0) = objRes.DistanceTo(objFish)
    End If
      k = k + 1
    Resume


End Sub

ОБНОВЛЕНИЕ: я включил большинство предложений от @winwaed и @Mike D, и мой код стал более точным и не содержит ошибок. Однако старая проблема встала в новой форме. Теперь, примерно после 10 000 итераций, код продолжает работу, но печатает расстояние ~10 000-й записи для каждой последующей записи. Я могу перезапустить код в проблемной точке, и он нормально найдет расстояния для этих записей. Почему это произошло? Я разместил свой обновленный код ниже.

Sub distance_from_res()

Dim oApp As MapPoint.Application
Dim k As Long 
Dim rc As Long 
Dim errors As Long

Dim dist As Double
Dim zipRes As Range
Dim coRes As Range
Dim coInt As Range
Dim distR As Range

Set zipRes = Sheets("Sheet1").Range("C2")
Set coRes = Sheets("Sheet1").Range("B2")
Set coInt = Sheets("Sheet1").Range("E2")
Set distR = Sheets("Sheet1").Range("G2")

k = 0
rc = Sheets("Sheet1").Range("F2", Sheets("Sheet1").Range("F2").End(xlDown)).Count
errors = 0

'Start MapPoint application.
Set oApp = CreateObject("MapPoint.Application.NA.11")
oApp.Visible = False
Set objMap = oApp.NewMap
Dim objResultsRes As MapPoint.FindResults
Dim objResultsInt As MapPoint.FindResults
Dim objRes As MapPoint.Location
Dim objInt As MapPoint.Location

Do While k < rc
    'Check results for Res Zip Code.  If good, set first result to objRes.  If not, check results for Res County,ST.  If good, set first result to objRes.  Else, set objRes to Nothing.
    Set objResultsRes = objMap.FindResults(zipRes.Offset(k, 0))
    If objResultsRes.ResultsQuality = geoFirstResultGood Then
        Set objRes = objResultsRes.Item(1)
    Else
        Set objResultsRes = Nothing
        Set objResultsRes = objMap.FindResults(coRes.Offset(k, 0))
        If objResultsRes.ResultsQuality = geoFirstResultGood Then
            Set objRes = objResultsRes.Item(1)
        Else
            If objResultsRes.ResultsQuality = geoAmbiguousResults Then
                Set objRes = objResultsRes.Item(1)
            Else
                Set objRes = Nothing
            End If
        End If
    End If

    Set objResultsInt = objMap.FindResults(coInt.Offset(k, 0))
    If objResultsInt.ResultsQuality = geoFirstResultGood Then
        Set objInt = objResultsInt.Item(1)
    Else
        If objResultsInt.ResultsQuality = geoAmbiguousResults Then
            Set objInt = objResultsInt.Item(1)
        Else
            Set objInt = Nothing
        End If
    End If

    On Error GoTo ErrDist
    distR.Offset(k, 0) = objRes.DistanceTo(objInt)

    k = k + 1
Loop

Exit Sub


ErrDist:
    errors = errors + 1
    Resume Next

End Sub

person Excellll    schedule 13.03.2011    source источник


Ответы (2)


MikeD прав с вашими опасными вызовами FindResults(). Однако есть лучший способ проверить результаты. «Коллекция FindResults» не является чистой коллекцией, но включает в себя дополнительные свойства под названием «ResultsQuality». Документы здесь:

http://msdn.microsoft.com/en-us/library/aa493061.aspx

Resultsquality возвращает перечисление GeoFindResultsQuality. Вы хотите проверить значения geoAllResultsGood и geFirstResultGood. Все остальные результаты должны давать ошибку некоторого результата. Обратите внимание, что ваш существующий код будет работать с (например) неоднозначными результатами, хотя маловероятно, что первый результат будет правильным. Также он может совпадать по штату или почтовому индексу (потому что это лучшее, что он может найти), что даст вам ошибочный результат. Используя ResultsQuality, вы можете обнаружить это.

Я бы еще проверил значение Count в качестве дополнительной проверки.

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

person winwaed    schedule 14.03.2011
comment
Спасибо за подсказку по результатам. Я подозреваю, что это поможет решить мою проблему в конечном итоге. Однако прошлой ночью я попытался использовать синтаксис object.ResultsQuality, и это привело к ошибке компиляции. Есть идеи, почему? - person Excellll; 18.03.2011
comment
Вероятно, опечатка или неправильная ссылка на объект — это трудно понять, не видя вашего кода. Вам понадобится что-то вроде myResults = myMap.FindResults(‹бла-бла›), за которым следует myResults.ResultsQuality - person winwaed; 18.03.2011
comment
Спасибо. Я понял, что пытался вызвать ResultsQuality из местоположения MapPoint, а не из объекта FindResults. Новая программа работает отлично! Использование ResultsQuality улучшило мои данные о расстоянии, а кэш расстояний сократил время выполнения примерно на 80%. Большая помощь! - person Excellll; 20.03.2011

Вы создаете несколько сложный объект диапазона (Диапазон -> Смещение -> Элемент). DIM объекты временного диапазона и делайте это по шагам, чтобы вы могли видеть, где именно возникает проблема.

tmpR1 = Sheets("i1_20041").Range("G2")
tmpR2 = tmpR1.Offset(k,0)

затем проверьте свойство .Count объекта .FindResult, прежде чем пытаться получить доступ к Item(1).... может быть, этот элемент не существует?!?

Debug.Print objMap.FindResult(tmpR2).Count

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

person MikeD    schedule 14.03.2011