Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

Consulta MySQL de geolocalização


O problema é que a forma como você está armazenando os dados no banco de dados não é adequada para o tipo de tarefa que está realizando. Usando Point valores em Geometry pontos de dados é o caminho a percorrer. Na verdade, codificou algo há mais de 4 anos para esse fim, mas com problemas para encontrá-lo. Mas esta postagem parece cobrir bem.

EDITAR Ok, encontrei meu código antigo, mas está se referindo a dados antigos de clientes que obviamente não posso compartilhar. Mas a chave para acelerar com coordenadas em bancos de dados é usar POINT dados armazenados na tabela do banco de dados com o tipo de GEOMETRY . Mais detalhes aqui no site oficial do MySQL. Como eu precisava de um motivo para revisitar esse tipo de código - e os conceitos - por um tempo, aqui está um script MySQL rápido que preparei para criar uma tabela de amostra com dados de amostra para transmitir os conceitos básicos. Depois de entender o que está acontecendo, ele abre muitas opções interessantes.

Também encontrei esta ótima/simples explicação também do conceito.

E encontrei outra ótima avaliação de dados espaciais no MySQL 5.6. Muitas informações excelentes sobre índices e desempenho. Especificamente em relação ao desempenho do índice espacial do MySQL:

E do outro lado:

E aqui estão meus scripts básicos de teste do MySQL para ajudar a ilustrar o conceito:
/* 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
;