sql >> Base de Datos >  >> RDS >> Mysql

Encuentre todos los códigos postales dentro de la distancia especificada de un código postal

Aquí hay algo que escribí hace bastante tiempo que puede guiarlo en la dirección correcta.

Mientras solicitó VB.Net, lo que realmente necesita es una consulta que haga un "Great Circle Distancia " cálculo para determinar la distancia entre dos puntos identificados por latitud y longitud.

Entonces, haciendo las siguientes suposiciones:

  1. Los datos de su código postal están en una sola tabla.
  2. Dicha tabla tiene atributos para latitud y longitud que son el centroide aproximado del código postal

Podría usar una consulta LINQ to SQL que produzca el conjunto de resultados deseado usando algo como esto

Const EARTH_RADIUS As Single = 3956.0883313286095
Dim radCvtFactor As Single = Math.PI / 180
Dim zipCodeRadius As Integer = <Your Radius Value>

Dim zipQry = From zc In db.ZipCodes 
             Where zc.Zip = "<Your Zip Code>" _
             Select zc.Latitude, 
                    zc.Longitude, 
                    ZipLatRads = RadCvtFactor * zc.Latitude, 
                    ZipLonRads = RadCvtFactor * zc.Longitude
Dim zipRslt = zipQry.SingleOrDefault()
If zipRslt IsNot Nothing Then
    Dim zcQry = From zc In db.ZipCodes _
                Where zc.Latitude >= (zipRslt.Latitude - 0.5) And zc.Latitude <= (zipRslt.Latitude + 0.5) _
                And zc.Longitude >= (zipRslt.Longitude - 0.5) And (zc.Longitude <= zipRslt.Longitude + 0.5) _
                And Math.Abs(EARTH_RADIUS * (2 * Math.Atan2(Math.Sqrt(Math.Pow(Math.Sin(((RadCvtFactor * zc.Latitude) - zipRslt.ZipLatRads) / 2), 2) + _
                Math.Cos(zipRslt.ZipLatRads) * Math.Cos(RadCvtFactor * zc.Latitude) * _
                Math.Pow(Math.Sin(((RadCvtFactor * zc.Longitude) - zipRslt.ZipLonRads) / 2), 2)), _
                Math.Sqrt(1 - Math.Pow(Math.Sin(((RadCvtFactor * zc.Latitude) - zipRslt.ZipLatRads) / 2), 2) + _
                Math.Cos(zipRslt.ZipLatRads) * Math.Cos(RadCvtFactor * zc.Latitude) * _
                Math.Pow(Math.Sin((RadCvtFactor * zc.Longitude) / 2), 2))))) <= zipCodeRadius _
                Select zc
End If

Parece complicado, porque lo es. Hay personas mucho más inteligentes aquí en SO que pueden explicar el algoritmo. Simplemente implementé esto a partir de un código SQL que encontré en Internet; no recuerdo de dónde. Una búsqueda en Google debería llevarlo allí.

La primera consulta (zipQry) devuelve la latitud y la longitud del código postal inicial en grados y radianes. Estos resultados se utilizan luego para ejecutar la segunda consulta.

La primera parte de la cláusula WHERE en la segunda consulta:

Where zc.Latitude >= (zipRslt.Latitude - 0.5) And zc.Latitude <= (zipRslt.Latitude + 0.5) _
And zc.Longitude >= (zipRslt.Longitude - 0.5) And (zc.Longitude <= zipRslt.Longitude + 0.5) _

Simplemente redujo la lista de códigos postales a examinar, lo que hizo que la consulta se ejecutara mucho más rápido. Agrega una cantidad arbitraria a la latitud y longitud para que no esté verificando todos los códigos postales en Ohio cuando busca un radio en California. El resto es parte del algoritmo de distancia del gran círculo antes mencionado.

Esto probablemente podría haberse hecho en una consulta para una mayor eficiencia, pero lo necesitaba de esta manera en ese momento, las razones ahora las perdí.