2010-03-09 11 views
8

ich die folgende MySQL-Tabelle erstellt haben, geografische Breite/Länge speichern Koordinaten für jeden Punkt mit einem Namen zusammen:Abfragepunkte innerhalb eines bestimmten Radius in 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; 

ich abfragen versuche:

  • alle Punkte innerhalb eines n Meilenradius eines bestimmten Punktes;
  • die Entfernung jedes zurück Punkt von dem gegebenen Punkt

Alle Beispiele beziehen ich gefunden habe, eine minimale Begrenzungsrechteck (MBR) und nicht als ein Radius zu verwenden. Die Tabelle enthält ungefähr 1 Million Punkte, daher muss dieser Bedarf so effizient wie möglich sein.

Antwort

3

Vielen Dank für Ihre Antworten.

Ich fand schließlich die Lösung bei http://www.movable-type.co.uk/scripts/latlong-db.html.

+0

Was haben Sie getan, um das zu beheben? Ich habe Schwierigkeiten mit der Entscheidung, entweder MySQL 5.7 Spacial-Funktionen zu verwenden oder die Haversine-Formel zu verwenden. – Jethro

2

Radius ist nicht effizient indexierbar. Sie sollten das Begrenzungsrechteck verwenden, um schnell die Punkte zu erhalten, nach denen Sie wahrscheinlich suchen, und dann Punkte außerhalb des Radius zu filtern.

+0

Danke für Ihre Antwort. Ist die Einschränkung des Radiusindex nur ein Problem in MySQL? Ich frage mich, ob PostgreSQL praktikabler sein könnte? Wie würde ich Punkte aus dem Begrenzungsrechteck eliminieren, die nicht innerhalb des Radius liegen? – gjb

+2

Nein, das ist ein generelles Problem. PostgreSQL macht es Ihnen leichter, weil Sie explizit fragen können, ob der Punkt in einem Kreis enthalten ist, und dies würde den Index so gut wie möglich verwenden, aber ich glaube, dass er zuerst nur eine rechteckige Suche verwenden würde. Ich kann keine MySQL-Funktion dafür sehen, aber Sie können einfach den Abstand zwischen der Mitte und dem Punkt berechnen. –

3

Für MySQL 5.7+

wir Gegeben hilft haben die folgende einfache Tabelle,

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

create spatial index example_lnglat 
    on example (lnglat); 

Mit den folgenden einfachen Daten,

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)); 

Sie würden die Punkte innerhalb eines bestimmten Bereichs von einem anderen Punkt (Anmerkung: Wir haben innerhalb eines Polygons suchen) erhalten mit der folgenden Kombination von st Funktionen:

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); 

Sie sollten so etwas wie dies als Ergebnis sehen:

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 

als Referenz auf Abstand, wenn wir die Einschränkung das Ergebnis für den Test entfernen Punkt sieht wie folgt aus:

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 

Anmerkung 1: das Feld lnglat genannt wird, da das ist die richtige Reihenfolge, wenn Sie von Punkten als (x, y) denken, und ist auch die um die meisten Funktionen (wie Punkt) Akzeptieren Sie den Parameter

Hinweis 2: Sie können räumliche Indizes nicht wirklich nutzen, wenn Sie Kreise verwenden würden; Beachten Sie auch, dass das Punktfeld so eingestellt werden kann, dass es null akzeptiert, aber räumliche Indizes können es nicht indizieren, wenn es nullfähig ist (alle Felder im Index müssen nicht null sein).

Anmerkung 3: ST_Buffer betrachtet wird (von der Dokumentation), schlecht zu sein für diesen Anwendungsfall

Anmerkung 4: die oben genannten Funktionen (insbesondere st_distance_sphere) sind so schnell dokumentiert, aber nicht unbedingt super präzise ; Wenn Ihre Daten super empfindlich sind, fügen Sie ein bisschen Wackelraum zur Suche hinzu und tun Sie etwas Feineinstellung zum Ergebnissatz

+0

Vielleicht verstehe ich das nicht, aber wenn ich 'st_contains' benutze, wird das nicht immer noch Berechnungen in einem kartesischen Flugzeug machen? Nicht sphärische Erde wie gewünscht? – Twig

+0

Hallo, ich benutze Ihren Code oben, ich habe eine Frage, was bedeutet @rangeKm/111? Wofür ist die 111? –