MySQL Guru o no, el problema es que a menos que encuentre una forma de filtrar varias filas, la distancia debe calcularse entre cada punto y cada ciudad...
Hay dos enfoques generales que pueden ayudar a la situación
- hacer que la fórmula de distancia sea más simple
- filtrar candidatos improbables en el radio de 100k de una ciudad determinada
Antes de entrar en estas dos vías de mejora, debe decidir el nivel de precisión deseado con respecto a esta distancia de 100 millas, también debe indicar qué área geográfica cubre la base de datos (es solo EE. UU. continental, etc.).
La razón de esto es que, si bien es más precisa numéricamente, la fórmula del Gran Círculo es muy costosa desde el punto de vista computacional. Otra forma de mejorar el rendimiento sería almacenar "coordenadas de cuadrícula" además (o en lugar de) las coordenadas de latitud/longitud.
Editar :
Algunas ideas sobre una fórmula más simple (pero menos precisa) :
Dado que estamos tratando con distancias relativamente pequeñas (y supongo que entre 30 y 48 grados Lat North), podemos usar la distancia euclidiana (o mejor aún, el cuadrado de la distancia euclidiana) en lugar de la fórmulas de trigonometría esférica más complicadas.
dependiendo del nivel de precisión esperado, incluso puede ser aceptable tener un solo parámetro para la distancia lineal para un grado completo de longitud, tomando algo promedio sobre el área considerada (digamos alrededor de 46 estatuto millas). La fórmula sería entonces
LatDegInMi = 69.0
LongDegInMi = 46.0
DistSquared = ((Lat1 - Lat2) * LatDegInMi) ^2 + ((Long1 - Long2) * LongDegInMi) ^2
Sobre la idea de una columna con información de cuadrícula para filtrar para limitar el número de filas considerado para el cálculo de la distancia.
A cada "punto" en el sistema, ya sea una ciudad u otro punto (? ubicaciones de entrega, ubicaciones de tiendas... lo que sea) se le asignan dos coordenadas enteras que definen el cuadrado de, digamos, 25 millas * 25 millas donde se encuentra el punto. Las coordenadas de cualquier punto dentro de las 100 millas del punto de referencia (una ciudad determinada) serán como máximo +/- 4 en la dirección x y +/- 4 en la dirección y. Entonces podemos escribir una consulta similar a la siguiente
SELECT city, state, latitude, longitude, COUNT(*)
FROM zipcodes Z
JOIN points P
ON P.GridX IN (
SELECT GridX - 4, GridX - 3, GridX - 2, GridX - 1, GridX, GridX +1, GridX + 2 GridX + 3, GridX +4
FROM zipcode ZX WHERE Z.id = ZX.id)
AND
P.GridY IN (
SELECT GridY - 4, GridY - 3, GridY - 2, GridY - 1, GridY, GridY +1, GridY + 2 GridY + 3, GridY +4
FROM zipcode ZY WHERE Z.id = ZY.id)
WHERE P.Status = A
AND ((Z.latitude - P.latitude) * LatDegInMi) ^2
+ ((Z.longitude - P.longitude) * LongDegInMi) ^2 < (100^2)
GROUP BY city,state,latitude,longitude;
Tenga en cuenta que LongDegInMi podría estar codificado (lo mismo para todas las ubicaciones dentro de EE. UU. continental) o provenir del registro correspondiente en la tabla de códigos postales. Del mismo modo, LatDegInMi podría estar codificado (no es necesario hacerlo variar, ya que, a diferencia del otro, es relativamente constante).
La razón por la que esto es más rápido es que para la mayoría de los registros en el producto cartesiano entre la tabla de códigos postales y la tabla de puntos, no calculamos la distancia en absoluto. Los eliminamos sobre la base de un valor de índice (GridX y GridY).
Esto nos lleva a la cuestión de qué índices SQL producir. Por supuesto, es posible que deseemos:- GridX + GridY + Estado (en la tabla de puntos) - GridY + GridX + estado (posiblemente) - Ciudad + Estado + latitud + longitud + GridX + GridY en la tabla de códigos postales
Una alternativa a las cuadrículas es "atar" los límites de latitud y longitud que consideraremos, en función de la latitud y longitud de una ciudad determinada. es decir, la condición JOIN se convierte en un rango en lugar de IN :
JOIN points P
ON P.latitude > (Z.Latitude - (100 / LatDegInMi))
AND P.latitude < (Z.Latitude + (100 / LatDegInMi))
AND P.longitude > (Z.longitude - (100 / LongDegInMi))
AND P.longitude < (Z.longitude + (100 / LongDegInMi))