MySQL Guru ou não, o problema é que, a menos que você encontre uma maneira de filtrar várias linhas, a distância precisa ser calculada entre cada ponto e cada cidade...
Existem duas abordagens gerais que podem ajudar a situação
- simplifique a fórmula da distância
- filtre candidatos improváveis para o raio de 100 mil de uma determinada cidade
Antes de entrar nessas duas vias de melhoria, você deve decidir sobre o nível de precisão desejado em relação a essa distância de 100 milhas, também deve indicar qual área geográfica é coberta pelo banco de dados (é apenas EUA continental etc.
A razão para isso é que, embora numericamente mais precisa, a fórmula do Grande Círculo é muito cara computacionalmente. Outra avenida de melhoria de desempenho seria armazenar "coordenadas de grade" de tipos em adição (ou em vez de) as coordenadas Lat/Long.
Editar :
Algumas ideias sobre uma fórmula mais simples (mas menos precisa) :
Como estamos lidando com distâncias relativamente pequenas, (e estou supondo entre 30 e 48 graus Lat North), podemos usar a distância euclidiana (ou melhor ainda o quadrado da distância euclidiana) em vez da fórmulas de trigonometria esférica mais complicadas.
dependendo do nível de precisão esperado, pode até ser aceitável ter um único parâmetro para a distância linear para um grau completo de longitude, tomando algo médio sobre a área considerada (digamos cerca de 46 estatuto milhas). A fórmula então se tornaria
LatDegInMi = 69.0
LongDegInMi = 46.0
DistSquared = ((Lat1 - Lat2) * LatDegInMi) ^2 + ((Long1 - Long2) * LongDegInMi) ^2
Sobre a ideia de colunas com informações de grade para filtrar para limitar o número de linhas considerado para o cálculo da distância.
Cada "ponto" no sistema, seja uma cidade, ou outro ponto (? locais de entrega, locais de armazenamento... qualquer que seja) é atribuído a duas coordenadas inteiras que definem o quadrado de, digamos, 25 milhas * 25 milhas onde se encontra o ponto. As coordenadas de qualquer ponto dentro de 100 milhas do ponto de referência (uma determinada cidade), serão no máximo +/- 4 na direção x e +/- 4 na direção y. Podemos então escrever uma consulta semelhante à seguinte
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;
Observe que o LongDegInMi pode ser codificado permanentemente (o mesmo para todos os locais nos EUA continentais) ou vir do registro correspondente na tabela de CEPs. Da mesma forma, LatDegInMi pode ser codificado (pouca necessidade de fazê-lo variar, pois ao contrário do outro é relativamente constante).
A razão pela qual isso é mais rápido é que para a maioria dos registros no produto cartesiano entre a tabela de CEPs e a tabela de pontos, não calculamos a distância. Nós os eliminamos com base em um valor de índice (o GridX e GridY).
Isso nos leva à questão de quais índices SQL produzir. Com certeza, podemos querer:- GridX + GridY + Status (na tabela de pontos)- GridY + GridX + status (possivelmente)- City + State + latitude + longitude + GridX + GridY na tabela de CEPs
Uma alternativa às grades é "limitar" os limites de latitude e longitude que consideraremos, com base na latitude e longitude de uma determinada cidade. ou seja, a condição JOIN se torna um intervalo em vez de um 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))