2009-11-28 11 views
35

Ho un database MySQL. Conservo le case nel database ed eseguo letteralmente solo 1 query sul database, ma ho bisogno che questa query sia eseguita super veloce, e questo è per restituire tutte le case all'interno di una casella quadrata geo latitudine & longitudine.Database: il modo migliore per eseguire le query sui dati di posizione geografica?

SELECT * FROM homes 
WHERE geolat BETWEEN ??? AND ??? 
AND geolng BETWEEN ??? AND ??? 

Come è il modo migliore per me per memorizzare i miei dati geografici in modo che possa svolgere questa interrogazione di visualizzare tutti a casa all'interno della scatola geolocalizzazione il più veloce?

In sostanza:

  • Am I utilizzando la migliore istruzione SQL per eseguire questa query il più veloce?
  • Esiste qualche altro metodo, magari non utilizzando nemmeno un database, perché io possa interrogare il modo più veloce un risultato di case entro limiti di geolocalizzazione in scatola?

In caso aiuta, ho includono il mio schema di tabella del database di seguito:

CREATE TABLE IF NOT EXISTS `homes` (
    `home_id` int(10) unsigned NOT NULL auto_increment, 
    `address` varchar(128) collate utf8_unicode_ci NOT NULL, 
    `city` varchar(64) collate utf8_unicode_ci NOT NULL, 
    `state` varchar(2) collate utf8_unicode_ci NOT NULL, 
    `zip` mediumint(8) unsigned NOT NULL, 
    `price` mediumint(8) unsigned NOT NULL, 
    `sqft` smallint(5) unsigned NOT NULL, 
    `year_built` smallint(5) unsigned NOT NULL, 
    `geolat` decimal(10,6) default NULL, 
    `geolng` decimal(10,6) default NULL, 
    PRIMARY KEY (`home_id`), 
    KEY `geolat` (`geolat`), 
    KEY `geolng` (`geolng`), 
) ENGINE=InnoDB ; 

UPDATE

Capisco fattore volontà spaziale nella curvatura della terra, ma io sono più interessato a restituire i dati geografici il più veloce. A meno che questi pacchetti di database spaziali non restituiscano più velocemente i dati, si consiglia di non consigliare estensioni spaziali. Grazie

UPDATE 2

Si prega di notare, che nessuno al di sotto ha davvero risposto alla domanda. Non vedo davvero l'ora di ricevere assistenza. Grazie in anticipo.

+0

Le coordinate UTM sono una scelta migliore: il mondo non è piatto, ma l'UTM incorpora un livello di appiattimento mentre Lat/Long non lo fa affatto. –

+1

Raccomando anche di leggere le funzionalità spaziali di MySQL: http://dev.mysql.com/doc/refman/5.0/en/spatial-extensions.html –

+0

Postgres è un'altra alternativa db con capacità spaziale, che consiglio di utilizzare piuttosto che MySQL : http://www.postgresql.org/ –

risposta

2

Se è davvero necessario andare per prestazioni, è possibile definire caselle di delimitazione per i dati e mappare le caselle di delimitazione precomputer agli oggetti al momento dell'inserimento e utilizzarli in un secondo momento per le query.

Se i gruppi di risultati sono ragionevolmente piccoli, è comunque possibile eseguire correzioni di precisione nella logica dell'applicazione (più semplice scalare orizzontalmente rispetto a un database) consentendo al tempo stesso di fornire risultati accurati.

Dai un'occhiata a geobox.py di Bret Slatkin che contiene un'ottima documentazione per l'approccio.

Si consiglia comunque di controllare PostgreSQL e PostGIS in confronto a MySQL se si intende eseguire query più complesse nel prossimo futuro.

+1

E questo è esattamente il motivo per cui non dovremmo usare i collegamenti su StackOverflow. Il tuo link è rotto – Sandor

+1

@ Grazie per avermi informato, ho adattato la risposta e rimosso il collegamento morto. – tosh

1

Gli indici utilizzati sono in effetti indici B-tree e supportano la parola chiave BETWEEN nella query. Ciò significa che l'ottimizzatore è in grado di utilizzare i tuoi indici per trovare le case nella tua "scatola". Tuttavia, non significa che utilizzerà sempre gli indici. Se si specifica un intervallo che contiene troppi "colpi", gli indici non verranno utilizzati.

+0

Quindi, userebbe min_latitude> = ??? max_latitude <= ??? essere migliori invece di usare BETWEEN? – HankW

+0

Dal manuale: Questo è equivalente all'espressione (min <= expr AND expr <= max) –

+0

cosa vuoi dire se ci sono troppi "colpi" che gli indici non saranno usati? Non capisco – HankW

0

Questo sembra abbastanza veloce. La mia unica preoccupazione sarebbe che userebbe un indice per ottenere tutti i valori entro 3 miglia dalla latitudine, quindi filtrare quelli per i valori entro 3 miglia dalla longitudine. Se capisco come funziona il sistema sottostante, puoi usare solo un INDICE per tabella, quindi l'indice su lat o long non ha valore.

Se si ha una grande quantità di dati, potrebbe ad accelerare per dare ogni quadrato 1x1 miglio un ID unico logico, e poi fare un ulteriore restrizione sul SELECT che (area = "23234/34234" O area = "23235/34234" O ...) per tutti i quadrati attorno al punto, quindi forzare il database a utilizzare quell'indice anziché il lat e il long. Quindi filtrerai solo meno chilometri quadrati di dati.

+0

Un indice per tabella? Lo confondi con la chiave primaria? –

+0

Voglio dire che quando si fa un SELECT, usa solo un indice per tabella in SELECT. –

+0

Ah .. Questo è un buon punto, ma pensi che la creazione di un indice composito possa fare la differenza? –

4

Ho avuto lo stesso problema e ho scritto un post in 3 parti. Questo era più veloce del geo index.

Intro, Benchmark, SQL

+0

Evert, come hai implementato Morton (valore z)? Il secondo articolo è appena saltato e non dice nulla su come hai calcolato il valore – HankW

+0

Il terzo in realtà.C'è una stored procedure – Evert

+0

Quello che non capisco è che quando eseguo la SELECT, come faccio a sapere qual è il valore del Morton da selezionare? – HankW

0

Homes? Probabilmente non ne avrai nemmeno diecimila. Basta usare un indice in memoria come STRTree.

12

C'è una buona carta sulle prestazioni di geolocalizzazione di MySQL here.

EDIT abbastanza sicuro che questo sta usando raggio fisso. Inoltre non sono sicuro al 100% che l'algoritmo per calcolare la distanza sia il più avanzato (cioè "trapanerà" attraverso la Terra).

Ciò che è significativo è che l'algoritmo è a buon mercato per darti un limite al parco giochi sul numero di file per effettuare una ricerca a distanza corretta.

+0

Sembra che l'utilizzo della stored procedure sulla diapositiva n. 14 sia promettente, ma non è chiaro per me se questo presuppone un raggio fisso. Sai se il raggio è fisso o no? Voglio essere in grado di passare nell'angolo della scatola (raggio) – HankW

+0

Ho bisogno di essere in grado di passare come argomento il raggio scatolato. Pensi che io possa quindi utilizzare il documento collegato come tale allora? – HankW

0

attaccare con il vostro approccio attuale non v'è un cambiamento che dovrebbe fare, Invece di indicizzazione geolat e geolong separatamente si dovrebbe avere un indice composto:

KEY `geolat_geolng` (`geolat`, `geolng`), 

Attualmente la vostra richiesta sarà tenuto unico vantaggio di una delle i due indici.

2

Ecco un trucco che ho utilizzato con un certo successo è quello di creare regioni arrotondate. Vale a dire, se si dispone di un luogo che è a 36.12345, -120.54321, e si desidera raggrupparlo con altre posizioni che si trovano entro una griglia di circa mezzo miglio (approssimativa), è possibile chiamare la sua regione 36.12x-120.54, e tutte le altre località con la stessa regione di arrotondamento si troveranno nella stessa casella.

Ovviamente, questo non ti dà un raggio pulito, cioè se il luogo che stai guardando è più vicino a un bordo rispetto a un altro. Tuttavia, con questo tipo di configurazione, è abbastanza facile calcolare le otto caselle che circondano la casella della posizione principale. Vale a dire:

[36.13x-120.55][36.13x-120.54][36.13x-120.53] 
[36.12x-120.55][36.12x-120.54][36.12x-120.53] 
[36.11x-120.55][36.11x-120.54][36.11x-120.53] 

Tirare tutte le sedi con corrispondenti etichette di arrotondamento e poi, una volta che li hai fuori del database, è possibile fare i vostri calcoli a distanza per determinare quali utilizzare.

0

Si potrebbe prendere in considerazione la creazione di una tabella separata 'GeoLocations' che ha una chiave primaria di ('geolat', 'geolng') e ha una colonna che contiene home_id se quella particolare geolocalizzazione ha una casa. Ciò dovrebbe consentire all'ottimizzatore di cercare un intervallo di posizioni geografiche che verranno ordinate su disco per un elenco di home_ids. È quindi possibile eseguire un join con la tabella 'case' per trovare informazioni su tali home_ids.

CREATE TABLE IF NOT EXISTS `GeoLocations` (
`geolat` decimal(10,6) NOT NULL, 
`geolng` decimal(10,6) NOT NULL, 
`home_id` int(10) NULL 
PRIMARY KEY (`geolat`,`geolng`) 
); 

SELECT GL.home_id 
FROM GeoLocations GL 
INNER JOIN Homes H 
ON GL.home_id = H.home_id 
WHERE GL.geolat between X and Y 
and GL.geolng between X and Y 
0

Da MySQL 5.7 mysql può usare geoindex come ST_Distance_Sphere() e ST_Contains() per migliorare le prestazioni.

Problemi correlati