Actualmente tengo poco menos de un millón de ubicaciones en una base de datos mysql, todas con información de longitud y latitud.
Estoy tratando de encontrar la distancia entre un punto y muchos otros puntos a través de una consulta. No es tan rápido como quiero que sea, especialmente con más de 100 golpes por segundo.
¿Hay una consulta más rápida o posiblemente un sistema más rápido que no sea mysql para esto? Estoy usando esta consulta:
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;
Nota: La distancia proporcionada es en millas . Si necesita kilómetros , use en 6371
lugar de 3959
.
Respuestas:
Cree sus puntos utilizando
Point
valores deGeometry
tipos de datos en laMyISAM
tabla. A partir de Mysql 5.7.5, lasInnoDB
tablas ahora también admitenSPATIAL
índices.Crea un
SPATIAL
índice sobre estos puntosUse
MBRContains()
para encontrar los valores:, o, en
MySQL 5.1
y arriba:Esto seleccionará todos los puntos aproximadamente dentro del cuadro
(@lat +/- 10 km, @lon +/- 10km)
.Esto en realidad no es una caja, sino un rectángulo esférico: segmento de la esfera unido a la latitud y la longitud. Esto puede diferir de un rectángulo simple en Franz Joseph Land , pero bastante cerca de él en la mayoría de los lugares habitados.
Aplique filtros adicionales para seleccionar todo dentro del círculo (no el cuadrado)
Posiblemente aplique un filtrado fino adicional para tener en cuenta la distancia del círculo grande (para distancias grandes)
fuente
@lon - 10 / ( 111.1 / cos(@lat))
(y sea la segunda en el par una vez que todo esté correcto.)cos(lon)
es preciso solo para distancias pequeñas. Ver janmatuschek.de/LatitudeLongitudeBoundingCoordinates111.(1)
km en un grado de latitud.mypoint
es el campo en la tabla que almacena las coordenadas.No es una respuesta específica de MySql, pero mejorará el rendimiento de su declaración sql.
Lo que efectivamente está haciendo es calcular la distancia a cada punto de la tabla, para ver si está dentro de las 10 unidades de un punto dado.
Lo que puede hacer antes de ejecutar este sql es crear cuatro puntos que dibujen un cuadro de 20 unidades a un lado, con su punto en el centro, es decir. (x1, y1). . . (x4, y4), donde (x1, y1) es (dado largo + 10 unidades, dado Lat + 10 unidades). . . (givenLong - 10units, givenLat -10 unidades). En realidad, solo necesitas dos puntos, arriba a la izquierda y abajo a la derecha, llámalos (X1, Y1) y (X2, Y2)
Ahora su declaración SQL usa estos puntos para excluir filas que definitivamente están a más de 10u de su punto dado, puede usar índices en las latitudes y longitudes, por lo que serán órdenes de magnitud más rápidos que los que tiene actualmente.
p.ej
El enfoque de cuadro puede devolver falsos positivos (puede recoger puntos en las esquinas del cuadro que están> 10u desde el punto dado), por lo que aún necesita calcular la distancia de cada punto. Sin embargo, esto nuevamente será mucho más rápido porque ha limitado drásticamente el número de puntos para probar a los puntos dentro del cuadro.
Yo llamo a esta técnica "Pensar dentro de la caja" :)
EDITAR: ¿Se puede poner esto en una declaración SQL?
No tengo idea de lo que mySql o Php es capaz, lo siento. No sé dónde es el mejor lugar para construir los cuatro puntos, o cómo podrían pasarse a una consulta mySql en Php. Sin embargo, una vez que tenga los cuatro puntos, no hay nada que le impida combinar su propia declaración SQL con la mía.
Sé que con MS SQL puedo construir una declaración SQL que declara cuatro flotantes (X1, Y1, X2, Y2) y los calcula antes de la declaración de selección "principal", como dije, no tengo idea si esto se puede hacer con MySql. Sin embargo, todavía estaría inclinado a construir los cuatro puntos en C # y pasarlos como parámetros a la consulta SQL.
Lo siento, no puedo ser más ayuda, si alguien puede responder las partes específicas de MySQL y Php de esto, no dude en editar esta respuesta para hacerlo.
fuente
La siguiente función MySQL se publicó en esta publicación de blog . No lo he probado mucho, pero por lo que obtuve de la publicación, si sus campos de latitud y longitud están indexados , esto puede funcionar bien para usted:
Uso de la muestra:
Suponiendo una tabla llamada
places
con camposlatitude
ylongitude
:fuente
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
Necesitaba resolver un problema similar (filtrando filas por distancia desde un solo punto) y combinando la pregunta original con respuestas y comentarios, se me ocurrió una solución que funciona perfectamente para mí tanto en MySQL 5.6 como en 5.7.
coordinates
es un campo con tipoPOINT
y tiene unSPATIAL
índice6371
es para calcular la distancia en kilómetros56.946285
es la latitud para el punto central24.105078
es la longitud para el punto central15
es la distancia máxima en kilómetrosEn mis pruebas, MySQL usa el índice SPATIAL en el
coordinates
campo para seleccionar rápidamente todas las filas que están dentro del rectángulo y luego calcula la distancia real para todos los lugares filtrados para excluir lugares de las esquinas de los rectángulos y dejar solo lugares dentro del círculo.Esta es la visualización de mi resultado:
Las estrellas grises visualizan todos los puntos en el mapa, las amarillas son las que devuelve MySQL query. Las estrellas grises dentro de las esquinas del rectángulo (pero fuera del círculo) fueron seleccionadas por
MBRContains()
y luego deseleccionadas por laHAVING
cláusula.fuente
si está utilizando MySQL 5.7. *, puede usar st_distance_sphere (POINT, POINT) .
fuente
Esta es la consulta de cálculo de distancia entre puntos en MySQL, la he usado en una base de datos larga, ¡funciona perfectamente! Nota: realice los cambios (nombre de la base de datos, nombre de la tabla, columna, etc.) según sus requisitos.
fuente
fuente
fuente
fuente
Una función MySQL que devuelve el número de metros entre las dos coordenadas:
Para devolver el valor en un formato diferente, reemplace el
6371000
en la función con el radio de la Tierra en su unidad de elección. Por ejemplo, kilómetros serían6371
y millas serían3959
.Para usar la función, simplemente llámela como lo haría con cualquier otra función en MySQL. Por ejemplo, si tuviera una mesa
city
, podría encontrar la distancia entre cada ciudad y todas las demás ciudades:fuente
El código completo con detalles sobre cómo instalar como complemento MySQL está aquí: https://github.com/lucasepe/lib_mysqludf_haversine
Publiqué esto el año pasado como comentario. Como amablemente @TylerCollier me sugirió publicar como respuesta, aquí está.
Otra forma es escribir una función UDF personalizada que devuelva la distancia de Haversine desde dos puntos. Esta función puede tomar en entrada:
Entonces podemos escribir algo como esto:
para obtener todos los registros con una distancia inferior a 40 kilómetros. O:
para buscar todos los registros con una distancia inferior a 25 pies.
La función principal es:
fuente
Se puede hacer una aproximación rápida, simple y precisa (para distancias más pequeñas) con una proyección esférica . Al menos en mi algoritmo de enrutamiento obtengo un aumento del 20% en comparación con el cálculo correcto. En el código Java se ve así:
No estoy seguro acerca de MySQL (¡lo siento!).
Asegúrese de conocer la limitación (el tercer parámetro de afirmar Equilibrios significa la precisión en kilómetros):
fuente
Aquí hay una descripción muy detallada de Geo Distance Search con MySQL, una solución basada en la implementación de Haversine Formula en mysql. La descripción completa de la solución con teoría, implementación y una mayor optimización del rendimiento. Aunque la parte de optimización espacial no funcionó correctamente en mi caso. http://www.scribd.com/doc/2569355/Geo-Distance-Search-with-MySQL
fuente
Eche un vistazo a Geo Distance Search con MySQL , una solución basada en la implementación de Haversine Formula en MySQL. Esta es una descripción completa de la solución con teoría, implementación y una mayor optimización del rendimiento. Aunque la parte de optimización espacial no funcionó correctamente en mi caso.
Noté dos errores en esto:
el uso de
abs
en la declaración select en p8. Simplemente lo omitíabs
y funcionó.la función de distancia de búsqueda espacial en p27 no se convierte a radianes ni multiplica la longitud por
cos(latitude)
, a menos que sus datos espaciales estén cargados con esto en consideración (no se puede distinguir por el contexto del artículo), pero su ejemplo en p26 indica que sus datos espacialesPOINT
no están cargados con radianes o grados.fuente
fuente
Usando mysql
Ver: https://andrew.hedges.name/experiments/haversine/
Ver: https://stackoverflow.com/a/24372831/5155484
Ver: http://www.plumislandmedia.net/mysql/haversine-mysql-nearest-loc/
NOTA:
LEAST
se utiliza para evitar valores nulos como un comentario sugerido en https://stackoverflow.com/a/24372831/5155484fuente