Gourou MySQL ou pas, le problème c'est qu'à moins de trouver un moyen de filtrer les différentes lignes, il faut calculer la distance entre chaque point et chaque ville...
Il existe deux approches générales qui peuvent aider la situation
- simplifier la formule de distance
- filtrer les candidats improbables dans un rayon de 100 k à partir d'une ville donnée
Avant d'aborder ces deux pistes d'amélioration, vous devez décider du niveau de précision souhaité par rapport à cette distance de 100 milles, également vous devez indiquer quelle zone géographique est couverte par la base de données (s'agit-il uniquement des États-Unis continentaux, etc.
La raison en est que, bien que plus précise numériquement, la formule du grand cercle est très coûteuse en calcul. Une autre possibilité d'amélioration des performances consisterait à stocker des "coordonnées de grille" en plus (ou à la place) des coordonnées Lat/Long.
Modifier :
Quelques idées sur une formule plus simple (mais moins précise) :
Puisque nous avons affaire à des distances relativement petites (et je suppose entre 30 et 48 degrés Lat Nord), nous pouvons utiliser la distance euclidienne (ou mieux encore le carré de la distance euclidienne) plutôt que le formules de trigonométrie sphérique plus compliquées.
selon le niveau de précision attendu, il peut même être acceptable d'avoir un seul paramètre pour la distance linéaire pour un degré complet de longitude, en prenant quelque chose de moyen sur la zone considérée (disons environ 46 statut milles). La formule deviendrait alors
LatDegInMi = 69.0
LongDegInMi = 46.0
DistSquared = ((Lat1 - Lat2) * LatDegInMi) ^2 + ((Long1 - Long2) * LongDegInMi) ^2
Sur l'idée d'une colonne avec info grille à filtrer pour limiter le nombre de lignes pris en compte pour le calcul de la distance.
Chaque « point » dans le système, que ce soit une ville ou un autre point (? Lieux de livraison, emplacements de magasin... peu importe) se voit attribuer deux coordonnées entières qui définissent le carré de, disons, 25 miles * 25 miles où se trouve le point. Les coordonnées de tout point à moins de 100 miles du point de référence (une ville donnée), seront au plus +/- 4 dans la direction x et +/- 4 dans la direction y. Nous pouvons alors écrire une requête similaire à la suivante
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;
Notez que le LongDegInMi peut être soit codé en dur (identique pour tous les emplacements des États-Unis continentaux), soit provenir de l'enregistrement correspondant dans le tableau des codes postaux. De même, LatDegInMi pourrait être codé en dur (pas besoin de le faire varier, car contrairement à l'autre, il est relativement constant).
La raison pour laquelle cela est plus rapide est que pour la plupart des enregistrements du produit cartésien entre la table des codes postaux et la table des points, nous ne calculons pas du tout la distance. Nous les éliminons sur la base d'une valeur d'indice (le GridX et le GridY).
Cela nous amène à la question de savoir quels index SQL produire. Bien sûr, on peut vouloir :- GridX + GridY + Status (sur la table des points)- GridY + GridX + status (éventuellement)- City + State + latitude + longitude + GridX + GridY sur la table des codes postaux
Une alternative aux grilles consiste à "limiter" les limites de latitude et de longitude que nous considérerons, en fonction de la latitude et de la longitude d'une ville donnée. c'est-à-dire que la condition JOIN devient une plage plutôt qu'un 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))