Сократите время вычислений, используя Microsoft Access для сортировки почтовых индексов по расстоянию - PullRequest
0 голосов
/ 05 января 2019

У меня есть вопрос об использовании Microsoft Access для сортировки моих клиентов (см. Первую таблицу ниже) по расстоянию от введенного пользователем почтового индекса. Я использую формулу расстояния большого круга (см. Код ниже), чтобы вычислить расстояние между почтовыми индексами. У меня есть таблица каждого текущего почтового индекса в США с его координатами широты и долготы (см. Вторую таблицу ниже). Функция берет почтовый индекс каждого клиента и захватывает широту и долготу из первой и второй таблиц и использует это рассчитать расстояние между каждым. Вот запущенный мной SQL-запрос, который вызывает GreatCircleDistance Формула:

PARAMETERS [Zip Code] IEEEDouble;
SELECT Clinics.Clinic, [US Zip Codes].ZIP, 
      [US Zip Codes].LAT, [US Zip Codes].LNG, 
      GreatCircleDistance([Zip Code],[LAT],[LNG],True,True) AS Distance
FROM [US Zip Codes] INNER JOIN Clinics ON [US Zip Codes].ZIP = Clinics.[Clinic ZIP];

Формула Великого окружного расстояния, используемая для этого запроса, показана ниже:

Private Const C_RADIUS_EARTH_KM As Double = 6370.97327862
Private Const C_RADIUS_EARTH_MI As Double = 3958.73926185
Private Const C_PI As Double = 3.14159265358979

Function GreatCircleDistance(ZipCode As Double, _
        Latitude2 As Double, Longitude2 As Double, _
        ValuesAsDecimalDegrees As Boolean, _
        ResultAsMiles As Boolean) As Double

Dim lat1 As Double
Dim lat2 As Double
Dim long1 As Double
Dim long2 As Double
Dim X As Long
Dim Delta As Double

If ValuesAsDecimalDegrees = True Then
    X = 1
Else
    X = 24
End If

' convert to decimal degrees
'POTENTIAL PROBLEM
lat1 = DLookup("[US Zip Codes].LAT", "[US Zip Codes]", "[US Zip Codes].ZIP = '" & ZipCode & "'") * X
long1 = DLookup("[US Zip Codes].LNG", "[US Zip Codes]", "[US Zip Codes].ZIP = '" & ZipCode & "'") * X
'POTENTIAL PROBLEM

lat2 = Latitude2 * X
long2 = Longitude2 * X

' convert to radians: radians = (degrees/180) * PI
lat1 = (lat1 / 180) * C_PI
lat2 = (lat2 / 180) * C_PI
long1 = (long1 / 180) * C_PI
long2 = (long2 / 180) * C_PI

' get the central spherical angle
Delta = ((2 * ArcSin(Sqr((Sin((lat1 - lat2) / 2) ^ 2) + _
Cos(lat1) * Cos(lat2) * (Sin((long1 - long2) / 2) ^ 2)))))

If ResultAsMiles = True Then
    GreatCircleDistance = Delta * C_RADIUS_EARTH_MI
Else
    GreatCircleDistance = Delta * C_RADIUS_EARTH_KM
End If

End Function



Function ArcSin(X As Double) As Double
    ' VBA doesn't have an ArcSin function. Improvise.
    ArcSin = Atn(X / Sqr(-X * X + 1))
End Function

Вот первая таблица со всеми моими клиентами. Я добавил только три для простоты, но в файле около 2000 записей:

Clinic            City        State     Clinic ZIP
Clinic #1       Lakeland        FL         33809
Clinic #2        Smyrna         TN         37167
Clinic #3       Kissimmee       FL         34747
...

Вот вторая таблица, в которой есть каждый почтовый индекс США. Всего зарегистрировано чуть более 41 000 почтовых индексов:

ID       ZIP         LAT           LNG
1       00501      40.8133      -73.0476      
2       00601       18.18       -66.7522
3       00602      18.3607      -67.1752
4       00603      18.4544       -67.122
...

Теперь этот метод работает, но он занимает много времени, чтобы вычислить расстояние между входным почтовым индексом и почтовым индексом каждого из 2000-тысячных клиентских почтовых индексов. Я думаю, что проблема в формуле Великого окружного расстояния, когда я использую функцию DLookUp , чтобы получить широту и долготу введенного пользователем почтового индекса. Кто-нибудь знает, как я могу уменьшить время вычислений для этого? Спасибо

Ответы [ 2 ]

0 голосов
/ 05 января 2019

Поскольку вам нужно сравнить две пары несвязанных координат, рассмотрим запрос перекрестного соединения, который в MS Access использует разделенные запятыми источники в предложении FROM. Затем перейдите в обе пары LAT и LNG в формулу расстояния.

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

PARAMETERS [ZipCodeParam] TEXT(255);
SELECT c.Clinic, us.ZIP, us.LAT, us.LNG, 
       p.ZIP, p.LAT, p.LNG,
       GreatCircleDistance(p.[LAT], p.[LNG], us.[LAT], us.[LNG], True, True) AS Distance
FROM [US Zip Codes] p, 
     ([US Zip Codes] us
      INNER JOIN Clinics c
          ON us.ZIP = c.[Clinic ZIP])
WHERE p.ZIP = ZipCodeParam;

Конечно, настройте параметры функции и удалите ненужные DLookUp вызовы:

Function GreatCircleDistance(Latitude1 As Double, Longitude1 As Double, _
        Latitude2 As Double, Longitude2 As Double, _
        ValuesAsDecimalDegrees As Boolean, _
        ResultAsMiles As Boolean) As Double

...

lat1 = Latitude1 * X
lng1 = Longitude1 * X

lat2 = Latitude2 * X
lng2 = Longitude2 * X

...

End Function
0 голосов
/ 05 января 2019

Возможно, проблема с кодом Dlookup. ваш код DLookup не выглядит правильно и не будет работать в моей системе. Этот код работал:

lat1 = DLookup ("LAT", "[US Zip Codes]", "ZIP =" & ZipCode) * X

lat1 = DLookup ("LNG", "[US Zip Codes]", "ZIP =" & ZipCode) * X

https://www.techonthenet.com/access/functions/domain/dlookup.php

...