sql >> Base de Datos >  >> RDS >> Mysql

Consulta MySQL de geolocalización

El problema es que la forma en que almacena los datos en la base de datos no es adecuada para el tipo de tarea que está realizando. Usando Point valores en Geometry puntos de datos es el camino a seguir. En realidad codificó algo hace más de 4 años para este propósito, pero tiene problemas para encontrarlo. Pero esta publicación parece cubrirlo bien.

EDITAR De acuerdo, encontré mi código anterior, pero se refiere a datos de clientes antiguos que obviamente no puedo compartir. Pero la clave para acelerar las coordenadas en las bases de datos es usar POINT datos almacenados en la tabla de la base de datos con el tipo de GEOMETRY . Más detalles aquí en el sitio oficial de MySQL. Ya que necesitaba una razón para revisar este tipo de código y los conceptos por un tiempo, aquí hay un script MySQL rápido que preparé para crear una tabla de muestra con datos de muestra para transmitir los conceptos básicos. Una vez que comprende lo que está sucediendo, se abren muchas opciones interesantes.

También encontré esta gran/simple explicación del concepto también.

Y encontré otra gran evaluación de datos espaciales en MySQL 5.6. Mucha información excelente sobre índices y rendimiento. Específicamente con respecto al rendimiento del índice espacial de MySQL:

Y al otro lado de eso:

Y aquí están mis scripts básicos de prueba de MySQL para ayudar a ilustrar el concepto:

/* Create the database `spatial_test` */
CREATE DATABASE `spatial_test` CHARACTER SET utf8 COLLATE utf8_general_ci;

/* Create the table `locations` in `spatial_test` */
CREATE TABLE `spatial_test`.`locations` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `coordinates` point NOT NULL,
  UNIQUE KEY `id` (`id`),
  SPATIAL KEY `idx_coordinates` (`coordinates`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;

/* Insert some test data into it. */
INSERT INTO `spatial_test`.`locations` (`id`, `coordinates`) VALUES (NULL, GeomFromText('POINT(27.174961 78.041822)'));
INSERT INTO `spatial_test`.`locations` (`id`, `coordinates`) VALUES (NULL, GeomFromText('POINT(27.985818 86.923596)'));
INSERT INTO `spatial_test`.`locations` (`id`, `coordinates`) VALUES (NULL, GeomFromText('POINT(44.427963 -110.588455)'));
INSERT INTO `spatial_test`.`locations` (`id`, `coordinates`) VALUES (NULL, GeomFromText('POINT(19.896766 -155.582782)'));
INSERT INTO `spatial_test`.`locations` (`id`, `coordinates`) VALUES (NULL, GeomFromText('POINT(40.748328 -73.985560)'));
INSERT INTO `spatial_test`.`locations` (`id`, `coordinates`) VALUES (NULL, GeomFromText('POINT(40.782710 -73.965310)'));

/* A sample SELECT query that extracts the 'latitude' & 'longitude' */
SELECT x(`spatial_test`.`locations`.`coordinates`) AS latitude, y(`spatial_test`.`locations`.`coordinates`) AS longitude FROM `spatial_test`.`locations`;

/* Another sample SELECT query calculates distance of all items in database based on GLength using another set of coordinates. */
SELECT GLength(LineStringFromWKB(LineString(GeomFromText(astext(PointFromWKB(`spatial_test`.`locations`.`coordinates`))), GeomFromText(astext(PointFromWKB(POINT(40.782710,-73.965310))))))) AS distance
FROM `spatial_test`.`locations`
;

/* Yet another sample SELECT query that selects items by using the Earth’s radius. The 'HAVING distance < 100' equates to a distance of less than 100 miles or kilometers based on what you set the query for. */
/* Earth’s diameter in kilometers: 6371 */
/* Earth’s diameter in miles: 3959 */
SELECT id, (3959 * acos(cos(radians(40.782710)) * cos(radians(x(`spatial_test`.`locations`.`coordinates`))) * cos(radians(y(`spatial_test`.`locations`.`coordinates`)) - radians(-73.965310)) + sin(radians(40.782710)) * sin(radians(x(`spatial_test`.`locations`.`coordinates`))))) AS distance 
FROM `spatial_test`.`locations`
HAVING distance < 100
ORDER BY id
;