J'ai actuellement un peu moins d'un million d'emplacements dans une base de données mysql, tous avec des informations de longitude et de latitude.
J'essaie de trouver la distance entre un point et de nombreux autres points via une requête. Ce n'est pas aussi rapide que je le souhaite, surtout avec plus de 100 coups par seconde.
Y a-t-il une requête plus rapide ou peut-être un système plus rapide autre que mysql pour cela? J'utilise cette requête:
SELECT
name,
( 3959 * acos( cos( radians(42.290763) ) * cos( radians( locations.lat ) )
* cos( radians(locations.lng) - radians(-71.35368)) + sin(radians(42.290763))
* sin( radians(locations.lat)))) AS distance
FROM locations
WHERE active = 1
HAVING distance < 10
ORDER BY distance;
Remarque: La distance fournie est en miles . Si vous avez besoin de kilomètres , utilisez 6371
plutôt 3959
.
Réponses:
Créez vos points en utilisant les
Point
valeurs desGeometry
types de données dans leMyISAM
tableau. Depuis Mysql 5.7.5, lesInnoDB
tableaux prennent désormais également en charge lesSPATIAL
indices.Créer un
SPATIAL
index sur ces pointsUtilisez
MBRContains()
pour trouver les valeurs:ou, dans
MySQL 5.1
et au-dessus:Cela sélectionnera tous les points approximativement dans la boîte
(@lat +/- 10 km, @lon +/- 10km)
.Ce n'est en fait pas une boîte, mais un rectangle sphérique: segment lié à la latitude et à la longitude de la sphère. Cela peut différer d'un rectangle simple sur la Terre François-Joseph , mais assez proche de lui dans la plupart des endroits habités.
Appliquer un filtrage supplémentaire pour sélectionner tout à l'intérieur du cercle (pas le carré)
Appliquer éventuellement un filtrage fin supplémentaire pour tenir compte de la distance du grand cercle (pour les grandes distances)
la source
@lon - 10 / ( 111.1 / cos(@lat))
(et sera la deuxième de la paire une fois que tout sera correct.cos(lon)
n'est également précise que pour les petites distances. Voir janmatuschek.de/LatitudeLongitudeBoundingCoordinates111.(1)
km dans un degré de latitude.mypoint
est le champ de la table qui stocke les coordonnées.Pas une réponse spécifique à MySql, mais cela améliorera les performances de votre instruction sql.
Ce que vous faites effectivement, c'est de calculer la distance à chaque point du tableau, pour voir si elle est à moins de 10 unités d'un point donné.
Ce que vous pouvez faire avant d'exécuter ce sql, c'est créer quatre points qui dessinent une boîte de 20 unités sur un côté, avec votre point au centre, c'est-à-dire. (x1, y1). . . (x4, y4), où (x1, y1) est (étant donné long + 10 unités, étant donné Lat + 10 unités). . . (givenLong - 10units, givenLat -10 units). En fait, vous n'avez besoin que de deux points, en haut à gauche et en bas à droite, appelez-les (X1, Y1) et (X2, Y2)
Maintenant, votre instruction SQL utilise ces points pour exclure les lignes qui sont définitivement à plus de 10u de votre point donné, elle peut utiliser des index sur les latitudes et les longitudes, les ordres de grandeur seront donc plus rapides que ceux que vous avez actuellement.
par exemple
L'approche boîte peut renvoyer des faux positifs (vous pouvez ramasser des points dans les coins de la boîte qui sont> 10u à partir du point donné), vous devez donc toujours calculer la distance de chaque point. Cependant, cela sera encore plus rapide car vous avez considérablement limité le nombre de points à tester aux points dans la boîte.
J'appelle cette technique "Penser à l'intérieur de la boîte" :)
EDIT: Cela peut-il être mis dans une seule instruction SQL?
Je n'ai aucune idée de ce dont mySql ou Php est capable, désolé. Je ne sais pas où le meilleur endroit est de construire les quatre points, ni comment ils pourraient être passés à une requête mySql dans Php. Cependant, une fois que vous avez les quatre points, rien ne vous empêche de combiner votre propre instruction SQL avec la mienne.
Je sais qu'avec MS SQL je peux construire une instruction SQL qui déclare quatre flottants (X1, Y1, X2, Y2) et les calcule avant l'instruction de sélection "principale", comme je l'ai dit, je ne sais pas si cela peut être fait avec MySql. Cependant, je serais toujours enclin à construire les quatre points en C # et à les transmettre en tant que paramètres à la requête SQL.
Désolé, je ne peux pas être plus d'aide, si quelqu'un peut répondre aux parties spécifiques de MySQL et Php, n'hésitez pas à modifier cette réponse pour le faire.
la source
La fonction MySQL suivante a été publiée sur ce billet de blog . Je ne l'ai pas beaucoup testé, mais d'après ce que j'ai rassemblé du post, si vos champs de latitude et de longitude sont indexés , cela peut bien fonctionner pour vous:
Exemple d'utilisation:
En supposant une table appelée
places
avec champslatitude
&longitude
:la source
SELECT ROUND(((ACOS(SIN(lat1 * PI() / 180) * SIN(lat2 * PI() / 180) + COS(lat1 * PI() / 180) * COS(lat2 * PI() / 180) * COS((lnt1 - lnt2) * PI() / 180)) * 180 / PI()) * 60 * 1.1515) * 1.609344 * 1000) AS distance
J'avais besoin de résoudre un problème similaire (filtrer les lignes en fonction de la distance à partir d'un seul point) et en combinant la question d'origine avec les réponses et les commentaires, j'ai trouvé une solution qui fonctionne parfaitement pour moi sur MySQL 5.6 et 5.7.
coordinates
est un champ de typePOINT
et a unSPATIAL
index6371
est pour le calcul de la distance en kilomètres56.946285
est la latitude pour le point central24.105078
est la longitude pour le point central15
est la distance maximale en kilomètresDans mes tests, MySQL utilise l'index SPATIAL sur le
coordinates
champ pour sélectionner rapidement toutes les lignes qui se trouvent dans un rectangle, puis calcule la distance réelle pour tous les endroits filtrés pour exclure les endroits des coins des rectangles et ne laisser que des endroits à l'intérieur du cercle.Voici la visualisation de mon résultat:
Les étoiles grises visualisent tous les points sur la carte, les étoiles jaunes sont celles retournées par la requête MySQL. Les étoiles grises à l'intérieur des coins du rectangle (mais à l'extérieur du cercle) ont été sélectionnées
MBRContains()
et désélectionnées par laHAVING
clause.la source
si vous utilisez MySQL 5.7. *, vous pouvez utiliser st_distance_sphere (POINT, POINT) .
la source
Ceci est la requête de calcul de distance entre des points dans MySQL, je l'ai utilisée dans une longue base de données, cela fonctionne parfaitement! Remarque: effectuez les modifications (nom de la base de données, nom de la table, colonne, etc.) selon vos besoins.
la source
la source
la source
la source
Une fonction MySQL qui renvoie le nombre de mètres entre les deux coordonnées:
Pour renvoyer la valeur dans un format différent, remplacez le
6371000
dans la fonction par le rayon de la Terre dans l'unité de votre choix. Par exemple, les kilomètres seraient6371
et les milles seraient3959
.Pour utiliser la fonction, il suffit de l'appeler comme vous le feriez pour n'importe quelle autre fonction dans MySQL. Par exemple, si vous aviez une table
city
, vous pourriez trouver la distance entre chaque ville et toutes les autres:la source
Le code complet avec des détails sur l'installation en tant que plugin MySQL est ici: https://github.com/lucasepe/lib_mysqludf_haversine
J'ai posté cela l'année dernière en tant que commentaire. Puisque gentiment @TylerCollier m'a suggéré de poster comme réponse, le voici.
Une autre façon consiste à écrire une fonction UDF personnalisée qui renvoie la distance haversine de deux points. Cette fonction peut prendre en entrée:
Nous pouvons donc écrire quelque chose comme ceci:
récupérer tous les enregistrements à une distance inférieure à 40 kilomètres. Ou:
pour récupérer tous les enregistrements à une distance inférieure à 25 pieds.
La fonction principale est:
la source
Une approximation rapide, simple et précise (pour les petites distances) peut être effectuée avec une projection sphérique . Au moins dans mon algorithme de routage, j'obtiens un boost de 20% par rapport au calcul correct. En code Java, cela ressemble à:
Pas sûr de MySQL (désolé!).
Assurez-vous de connaître la limitation (le troisième paramètre d'assertEquals signifie la précision en kilomètres):
la source
Voici une description très détaillée de Geo Distance Search avec MySQL, une solution basée sur l'implémentation de Haversine Formula sur mysql. La description complète de la solution avec la théorie, la mise en œuvre et l'optimisation des performances. Bien que la partie d'optimisation spatiale ne fonctionne pas correctement dans mon cas. http://www.scribd.com/doc/2569355/Geo-Distance-Search-with-MySQL
la source
Lisez la recherche de distance géographique avec MySQL , une solution basée sur la mise en œuvre de la formule Haversine sur MySQL. Il s'agit d'une description complète de la solution avec théorie, implémentation et optimisation des performances. Bien que la partie d'optimisation spatiale ne fonctionne pas correctement dans mon cas.
J'ai remarqué deux erreurs:
l'utilisation de
abs
dans l'instruction select à la p8. J'ai juste omisabs
et cela a fonctionné.la fonction de distance de recherche spatiale sur p27 ne se convertit pas en radians ou multiplie la longitude par
cos(latitude)
, sauf si ses données spatiales sont chargées avec cela en considération (ne peut pas le dire à partir du contexte de l'article), mais son exemple sur p26 indique que ses données spatiales nePOINT
sont pas chargées avec radians ou degrés.la source
la source
Utiliser mysql
Voir: https://andrew.hedges.name/experiments/haversine/
Voir: https://stackoverflow.com/a/24372831/5155484
Voir: http://www.plumislandmedia.net/mysql/haversine-mysql-nearest-loc/
REMARQUE:
LEAST
est utilisé pour éviter les valeurs nulles comme commentaire suggéré sur https://stackoverflow.com/a/24372831/5155484la source