2010-03-09 10 views
8

J'ai créé la table MySQL suivante pour stocker les coordonnées latitude/longitude avec un nom pour chaque point:des points d'interrogation dans un rayon donné dans MySQL

CREATE TABLE `points` (
    `id` int(10) unsigned NOT NULL AUTO_INCREMENT, 
    `name` varchar(128) NOT NULL, 
    `location` point NOT NULL, 
    PRIMARY KEY (`id`), 
    SPATIAL KEY `location` (`location`) 
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1; 

Je suis en train d'interroger:

  • tous les points situés dans un rayon de mile d'un point donné;
  • la distance de chaque point de retour à partir du point donné

Tous les exemples I ai trouvé référer à l'aide d'un rectangle de délimitation minimum (MBR) plutôt que d'un rayon. La table contient environ 1 million de points, donc ce besoin doit être aussi efficace que possible.

Répondre

2

Le rayon n'est pas indexable efficacement. Vous devez utiliser le rectangle de délimitation pour obtenir rapidement les points que vous recherchez probablement, puis filtrer les points en dehors du rayon.

+0

Merci pour votre réponse. La limitation d'index de rayon est-elle un problème dans MySQL? Je me demande si PostgreSQL pourrait être plus pratique? Comment éliminer les points du rectangle englobant qui ne se trouvent pas dans le rayon? – gjb

+2

Non, c'est un problème général. PostgreSQL vous facilite la tâche, car vous pouvez demander explicitement si le point est contenu dans un cercle et utiliser l'index aussi bien que possible, mais je crois qu'il n'utiliserait aussi qu'une recherche rectangulaire en premier. Je ne vois pas de fonction MySQL pour cela, mais vous pouvez simplement calculer la distance entre le centre et le point. –

3

Pour MySQL 5.7+

Étant donné que nous avoir le tableau simple suivant,

create table example (
    id bigint not null auto_increment primary key, 
    lnglat point not null 
); 

create spatial index example_lnglat 
    on example (lnglat); 

Avec les données simples suivantes,

insert into example (lnglat) 
values 
(point(-2.990435, 53.409246)), 
(point(-2.990037, 53.409471)), 
(point(-2.989736, 53.409676)), 
(point(-2.989554, 53.409797)), 
(point(-2.989350, 53.409906)), 
(point(-2.989178, 53.410085)), 
(point(-2.988739, 53.410309)), 
(point(-2.985874, 53.412656)), 
(point(-2.758019, 53.635928)); 

Vous obtiendrez des points dans une plage donnée d'un autre point (note: nous devons rechercher à l'intérieur d'un polygone) avec la combinaison suivante de fonctions st:

set @px = -2.990497; 
set @py = 53.410943; 
set @range = 150; -- meters 
set @rangeKm = @range/1000; 

set @search_area = st_makeEnvelope (
    point((@px + @rangeKm/111), (@py + @rangeKm/111)), 
    point((@px - @rangeKm/111), (@py - @rangeKm/111)) 
); 

select id, 
     st_x(lnglat) lng, 
     st_y(lnglat) lat, 
     st_distance_sphere(point(@px, @py), lnglat) as distance 
    from example 
where st_contains(@search_area, lnglat); 

Vous devriez voir quelque chose comme ceci comme résultat:

3 -2.989736 53.409676 149.64084252776277 
4 -2.989554 53.409797 141.93232714661812 
5 -2.98935 53.409906 138.11516275402533 
6 -2.989178 53.410085 129.40289289527473 

pour référence sur la distance, si on enlève la contrainte du résultat pour le test le point ressemble à ceci:

1 -2.990435 53.409246 188.7421181457556 
2 -2.990037 53.409471 166.49406509160158 
3 -2.989736 53.409676 149.64084252776277 
4 -2.989554 53.409797 141.93232714661812 
5 -2.98935 53.409906 138.11516275402533 
6 -2.989178 53.410085 129.40289289527473 
7 -2.988739 53.410309 136.1875540498202 
8 -2.985874 53.412656 360.78532732013963 
9 -2.758019 53.635928 29360.27797292756 

Note 1: le champ est appelé lnglat puisque c'est l'ordre correct si vous pensez des points que (x, y) et est également l'ordre la plupart des fonctions (comme point) accepter le paramètre

Remarque 2: vous ne pouvez pas réellement tirer parti des index spatiaux si vous utilisiez des cercles; Notez également que le champ de point peut être défini pour accepter null, mais les index spatiaux ne peuvent pas l'indexer s'il est nullable (tous les champs de l'index doivent être non null).

Note 3: ST_Buffer est considéré (par la documentation) pour être mauvais pour ce cas d'utilisation

Note 4: les fonctions ci-dessus (en st_distance_sphere particulier) sont documentés aussi vite, mais pas nécessairement super précis ; Si vos données sont super sensibles à cela ajouter un peu de marge de manœuvre à la recherche et faire un réglage fin à l'ensemble des résultats

+0

Peut-être que je suis un malentendu, mais en utilisant 'st_contains' n'est-ce pas encore faire des calculs dans un plan cartésien? Terre non sphérique comme on le souhaite? – Twig

+0

Bonjour, J'utilise votre code ci-dessus, j'ai une question, que signifie @rangeKm/111? À quoi sert le 111? –