come trovare in modo efficiente 20 punti più vicini [chiuso]


9

Di 'che voglio trovare 20 affari più vicini a me.

My table structure is like this:

    BusinessID  varchar(250)    utf8_unicode_ci         No  None        Browse distinct values  Change  Drop    Primary     Unique  Index   Fulltext
    Prominent   double          No  None        Browse distinct values  Change  Drop    Primary     Unique  Index   Fulltext
    LatLong     point           No  None        Browse distinct values  Change  Drop    Primary     Unique  Index   Fulltext
    FullTextSearch  varchar(600)    utf8_bin        No  None        Browse distinct values  Change  Drop    Primary     Unique  Index   Fulltext
With selected: Check All / Uncheck All With selected:
Print viewPrint view Propose table structurePropose table structureDocumentation
Add new fieldAdd field(s) At End of Table At Beginning of Table After
Indexes: Documentation
Action  Keyname Type    Unique  Packed  Field   Cardinality Collation   Null    Comment
Edit    Drop    PRIMARY BTREE   Yes No  BusinessID  1611454 A       
Edit    Drop    Prominent   BTREE   No  No  Prominent   0   A       
Edit    Drop    LatLong BTREE   No  No  LatLong (25)    0   A       
Edit    Drop    sx_mytable_coords   SPATIAL No  No  LatLong (32)    0   A       
Edit    Drop    FullTextSearch  FULLTEXT    No  No  FullTextSearch  0           

Ci sono 1,6 milioni di biz. Ovviamente è stupido calcolare la distanza per tutti e poi ordinarla.

È qui che entra in gioco l'indice geografico spaziale?

Quindi quale comando SQL devo lanciare?

Nota:

  1. Sto usando l'indice spaziale mysql myisam . Tuttavia non l'ho specificato prima. Accetterò quindi coloro che risponderanno per mostrare il mio apprezzamento e farti un'altra domanda.
  2. Non voglio calcolare la distanza per l'intero tavolo
  3. Non voglio calcolare la distanza per nessuna regione che sia ancora inefficiente
  4. Voglio calcolare la distanza per un numero ragionevole di punti perché voglio ordinare i punti per distanza ed essere in grado di visualizzare i punti 1-20, 21-40, 41-60, ecc.

3
cross post dba.stackexchange.com/questions/19595/… (Sembra anche male juju avere una domanda in cui ogni risposta si rivolge a PostGIS)
Evan Carroll

Risposte:


7

Le query spaziali sono sicuramente la cosa da usare.

Con PostGIS vorrei prima provare qualcosa di semplicistico come questo e modificare la gamma secondo necessità:

SELECT * 
FROM table AS a
WHERE ST_DWithin (mylocation, a.LatLong, 10000) -- 10km
ORDER BY ST_Distance (mylocation, a.LatLong)
LIMIT 20

Ciò confronterebbe i punti (in realtà i loro riquadri di delimitazione) usando l'indice spaziale, quindi dovrebbe essere veloce. Un altro approccio che viene in mente è buffering della posizione e quindi intersecare quel buffer con i dati originali, che potrebbe essere ancora più efficiente.


9

Se tutto ciò che stai cercando sono ricerche nei punti di prossimità (query dei vicini più vicini), non ti consigliamo di utilizzare i vecchi ST_DWithin o ST_Distance + ORDER BY.

Non più.

Ora che PostGIS 2.0 è stato spedito, dovresti utilizzare il supporto dell'indice knngist (una funzione PostgreSQL nativa). Saranno ordini di grandezza più veloci.

Un estratto da questo post di blog che descrive come usare knn gist senza PostGIS :

$ create table test ( position point );

CREATE TABLE
Table created. Now let’s insert some random points:
$ insert into test (position) select point( random() * 1000, random() * 1000) from generate_series(1,1000000);

INSERT 0 1000000
1 million points should be enough for my example. All of them have both X and Y in range <0, 1000). Now we just need the index:
$ create index q on test using gist ( position );

CREATE INDEX
And we can find some rows close to center of the points cloud:
$ select *, position <-> point(500,500) from test order by position <-> point(500,500) limit 10;

              position               |     ?column?

-------------------------------------+-------------------

 (499.965638387948,499.452529009432) | 0.548548271254899

 (500.473062973469,500.450353138149) |  0.65315122744144

 (500.277776736766,500.743471086025) | 0.793668174518778

 (499.986605718732,500.844359863549) | 0.844466095200968

 (500.858531333506,500.130807515234) | 0.868439207229501

 (500.96702715382,499.853323679417)  | 0.978087654172406

 (500.975443981588,500.170825514942) | 0.990289007195055

 (499.201623722911,499.368405900896) |  1.01799596553335

 (498.899147845805,500.683960970491) |  1.29602394829404

 (498.38217580691,499.178630765527)  |  1.81438764851559

(10 rows)
And how about speed?
$ explain analyze select *, position <-> point(500,500) from test order by position <-> point(500,500) limit 10;

                                                        QUERY PLAN

--------------------------------------------------------------------------------------------------------------------------

 Limit  (cost=0.00..0.77 rows=10 width=16) (actual time=0.164..0.475 rows=10 loops=1)

   ->  Index Scan using q on test  (cost=0.00..76512.60 rows=1000000 width=16) (actual time=0.163..0.473 rows=10 loops=1)

         Order By: ("position" <-> '(500,500)'::point)

 Total runtime: 0.505 ms

(4 rows)

Abbastanza interessante, l'indice traversal restituirà le caratteristiche in ordine di prossimità, quindi non c'è bisogno di fare un ordinamento (cioè ordinare per) per i risultati!

Tuttavia, se si desidera utilizzarlo insieme a PostGIS, ora è davvero semplice. Segui queste istruzioni .

La parte rilevante è questa:

SELECT name, gid
FROM geonames
ORDER BY geom <-> st_setsrid(st_makepoint(-90,40),4326)
LIMIT 10;

Ma non crederci sulla parola. Tempo tu stesso :)


Questa sarà una buona risposta. Tuttavia, sto usando mysql myisam. Ho dimenticato di aggiungerlo.
user4951

Quindi +1 ma non posso selezionarlo come mia risposta. Devo creare un'altra domanda?
user4951

@JimThio MySQL non ha un indice del vicino più vicino, quindi dovrai fare affidamento sull'approccio simile a PostGIS prima che ci fosse una query del vicino più vicino (ST_Dwithin con ORDER BY ST_Distance). Bentornato nel Medioevo :)
Ragi Yaser Burhum,

Quindi devo andare a Mongodb? Lasciami indovinare. Qual è il punto di avere un indice spaziale su mysql se non riesci nemmeno a fare la cosa più semplice come trovare 20 punti più vicini?
user4951

1
Puoi trovare il punto più vicino usando una finestra. Lo stesso vale per qualsiasi altro database spaziale come descritto da @lynxlynxlynx. Puoi continuare ad aumentare la finestra moltiplicandola per due. Sì, lo stesso vale per Mongo o qualsiasi altro database. Il punto è che riduci la maggior parte delle altre funzionalità. Inoltre, tutti sanno che fino a poco tempo fa, MySQL non è mai stato un serio contendente per qualcosa di spaziale.
Ragi Yaser Burhum,

8

Con PostGIS 2.0 su PostgreSQL 9.1, è possibile utilizzare l'operatore vicino più vicino indicizzato KNN , ad esempio:

SELECT *, geom <-> ST_MakePoint(-90, 40) AS distance
FROM table
ORDER BY geom <-> ST_MakePoint(-90, 40)
LIMIT 20 OFFSET 0;

Quanto sopra dovrebbe richiedere entro pochi millisecondi.

Per i successivi multipli di 20, modificare per OFFSET 20, OFFSET 40, ecc ...


Potrei sapere qual è il significato di <->? Grazie.
Northtree,

<->è un operatore che restituisce la distanza 2D.
Mike T,

1

MySQL Spatial

Tutti qui ti stanno dicendo come farlo con PostgreSQL usando KNN, senza dirti i vantaggi. Utilizzando MySQL non è possibile determinare il vicino più vicino senza calcolare la distanza per tutti i vicini. È estremamente lento. Con PostgreSQL questo può essere fatto su un indice. Né MySQL né MariaDB attualmente supportano KNN

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.