cara menemukan 20 titik terdekat secara efisien [ditutup]


9

Katakanlah saya ingin menemukan 20 bisnis terdekat di dekat saya.

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           

Ada 1,6 juta biz. Tentu saja bodoh menghitung jarak untuk mereka semua dan mengurutkannya.

Di situlah indeks geo spasial menendang ke kanan?

Jadi apa SQL comman yang harus saya lemparkan?

catatan:

  1. Saya menggunakan indeks spasial mysql myisam . Namun saya tidak menentukan ini sebelumnya. Jadi saya akan menerima mereka yang menjawabnya untuk menunjukkan penghargaan saya dan mengajukan pertanyaan lain.
  2. Saya tidak ingin menghitung jarak untuk seluruh tabel
  3. Saya tidak ingin menghitung jarak untuk wilayah mana pun yang masih tidak efisien
  4. Saya ingin menghitung jarak untuk jumlah poin yang masuk akal karena saya ingin mengurutkan poin berdasarkan jarak dan dapat menampilkan titik 1-20, 21-40, 41-60, dll.

3
cross post dba.stackexchange.com/questions/19595/… (Tampaknya juga juju yang buruk memiliki pertanyaan di mana setiap jawaban ditujukan pada PostGIS)
Evan Carroll

Jawaban:


7

Kueri spasial jelas merupakan hal yang harus digunakan.

Dengan PostGIS pertama-tama saya akan mencoba sesuatu yang sederhana seperti ini dan mengubah kisaran sesuai kebutuhan:

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

Ini akan membandingkan titik (sebenarnya kotak pembatas mereka) menggunakan indeks spasial, jadi harus cepat. Pendekatan lain yang muncul dalam pikiran adalah buffering lokasi Anda dan kemudian memotong buffer itu dengan data asli, yang mungkin bahkan lebih efisien.


9

Jika semua yang Anda cari adalah pencarian titik kedekatan (pertanyaan tetangga terdekat), maka Anda tidak ingin menggunakan ST_DWithin atau ST_Distance + ORDER BYs lama untuk itu.

Tidak lagi.

Sekarang setelah PostGIS 2.0 dikirimkan, Anda harus menggunakan dukungan indeks knngist (fitur PostgreSQL asli). Ini akan menjadi urutan besarnya lebih cepat.

Kutipan dari entri blog ini yang menjelaskan cara menggunakan knn gist tanpa 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)

Cukup menarik, indeks traversal akan mengembalikan fitur dalam urutan kedekatan, jadi tidak perlu melakukan pengurutan (yaitu pesanan oleh) untuk hasilnya!

Namun, jika Anda ingin menggunakannya bersama PostGIS, sekarang sangat mudah. Cukup ikuti instruksi ini .

Bagian yang relevan adalah ini:

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

Tapi jangan terima kata-kataku. Waktunya sendiri :)


Ini akan menjadi jawaban yang bagus. Namun, saya menggunakan mysql myisam. Saya lupa menambahkan itu.
user4951

Jadi +1 tetapi saya tidak dapat memilih ini sebagai jawaban saya. Haruskah saya membuat pertanyaan lain?
user4951

@JimThio MySQL tidak memiliki indeks tetangga terdekat sehingga Anda harus mengandalkan pendekatan seperti PostGIS sebelum ada permintaan tetangga terdekat (ST_Dwithin dengan ORDER BY ST_Distance). Selamat datang kembali ke abad pertengahan :)
Ragi Yaser Burhum

Jadi saya harus pergi ke mongodb? Biar kutebak. Apa gunanya memiliki indeks spasial di mysql jika Anda bahkan tidak dapat melakukan hal paling sederhana seperti menemukan 20 poin terdekat?
user4951

1
Anda dapat menemukan titik terdekat menggunakan jendela. Hal yang sama berlaku untuk basis data spasial lainnya seperti yang dijelaskan oleh @lynxlynxlynx. Anda dapat terus meningkatkan jendela dengan mengalikannya dengan dua. Ya, hal yang sama berlaku untuk Mongo atau basis data lainnya. Intinya adalah Anda mengurangi sebagian besar fitur lainnya. Selain itu, semua orang tahu bahwa baru-baru ini, MySQL tidak pernah menjadi pesaing serius untuk apa pun spasial.
Ragi Yaser Burhum

8

Dengan PostGIS 2.0 di PostgreSQL 9.1, Anda dapat menggunakan KNN yang diindeks operator tetangga terdekat , misalnya:

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

Pertanyaan di atas akan dicari dalam beberapa milidetik.

Untuk kelipatan berikutnya 20, memodifikasi OFFSET 20, OFFSET 40, dll ...


Bisakah saya tahu apa artinya <->? Terima kasih.
northtree

<->adalah operator yang mengembalikan jarak 2D.
Mike T

1

MySQL Spatial

Semua orang di sini memberi tahu Anda cara melakukannya dengan PostgreSQL menggunakan KNN, tanpa memberi tahu Anda kelebihannya. Menggunakan MySQL Anda tidak dapat menentukan tetangga terdekat tanpa menghitung jarak untuk semua tetangga. Itu sangat lambat. Dengan PostgreSQL ini dapat dilakukan pada indeks. Baik, MySQL maupun MariaDB saat ini mendukung KNN

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.