як ефективно знайти 20 найближчих точок [закрито]


9

Скажіть, я хочу знайти біля себе 20 найближчих підприємств.

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           

Є 1,6 мільйона біз. Звичайно, нерозумно обчислювати відстань для всіх, а потім сортувати.

Ось де гео просторовий індекс впадає прямо?

То який SQL-комман потрібно мені віддати?

Примітка:

  1. Я використовую просторовий індекс mysql myisam . Однак я цього раніше не вказував. Тому я прийму тих, хто відповість на це, щоб висловити свою вдячність і задати інше запитання.
  2. Я не хочу обчислювати відстань для всієї таблиці
  3. Я не хочу обчислювати відстань у будь-якому регіоні, який досі є неефективним
  4. Я хочу обчислити відстань за розумну кількість балів, тому що я хочу сортувати точки за відстанню і мати змогу відображати точки 1-20, 21-40, 41-60 тощо.

3
перехресний пост dba.stackexchange.com/questions/19595/… (Також здається поганим дзюджу виникнути питання, де кожна відповідь стосується PostGIS)
Еван Керролл

Відповіді:


7

Просторові запити, безумовно, є річ, яку потрібно використовувати.

З PostGIS я спершу спробую щось спрощене на кшталт цього і змінити діапазон у міру необхідності:

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

Це дозволило б порівняти точки (фактично їх обмежувальні поля) за допомогою просторового індексу, тому це повинно бути швидким. Ще один підхід, який спадає на думку, - це буферизація вашого місця розташування, а потім перетинання цього буфера з оригінальними даними, що може бути ще ефективнішим.


9

Якщо ви шукаєте лише пошук точки близькості (найближчі запити сусідів), ви не хочете використовувати для цього старі ST_DWithin або ST_Distance + ORDER BY.

Більше не.

Тепер, коли PostGIS 2.0 постачається, вам слід використовувати підтримку індексу knngist (нативна функція PostgreSQL). Це буде на порядок швидше.

Уривок із цього запису в блозі, який описує, як використовувати knn gist без 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)

Досить цікаво, що обхід індексу поверне функції в порядку близькості, тому не потрібно робити сортування (тобто замовлення за) для результатів!

Однак якщо ви хочете використовувати його поряд із PostGIS, зараз це дуже просто. Просто дотримуйтесь цих інструкцій .

Відповідна частина така:

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

Але не сприймай мого слова. Час сам :)


Це буде гарною відповіддю. Однак я використовую mysql myisam. Я забув додати це.
user4951

Отже +1, але я не можу вибрати це як свою відповідь. Чи варто створити інше запитання?
user4951

@JimThio MySQL не має індексу найближчого сусіда, тому вам доведеться покластися на підхід, подібний до PostGIS, до того, як з’явився найближчий запит сусіда (ST_Dwithin з ORDER BY ST_Distance). Ласкаво просимо до середніх віків :)
Рагі Ясер Бурхум

Тож я мушу поїхати до mongodb? Дай вгадаю. Який сенс мати просторовий індекс у mysql, якщо ви навіть не можете зробити найпростішу річ, як знайти 20 найближчих точок?
user4951

1
Ви можете знайти найближчу точку за допомогою вікна. Те саме стосується будь-якої іншої просторової бази даних, як описано в @lynxlynxlynx. Ви можете продовжувати збільшувати вікно, помноживши його на два. Так, це стосується Монго чи будь-якої іншої бази даних. Справа в тому, що ви скоротили більшість інших функцій. Крім того, всім відомо, що донедавна MySQL ніколи не був серйозним претендентом на що-небудь просторове.
Рагі Ясер Бурхум

8

З PostGIS 2.0 на PostgreSQL 9.1 ви можете використовувати найближчий сусідський оператор , індексований KNN , наприклад:

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

Вищезазначене має запитувати протягом декількох мілісекунд.

В протягом наступних кратних 20, змінити до OFFSET 20, OFFSET 40і т.д ...


Чи можу я знати, в чому сенс <->? Дякую.
northtree

<->- оператор, який повертає 2D відстань.
Майк Т

1

MySQL Spatial

Усі тут розповідають, як це зробити з PostgreSQL за допомогою KNN, не розповідаючи вам про переваги. Використовуючи MySQL, ви не можете визначити найближчого сусіда без обчислення відстані для всіх сусідів. Це надзвичайно повільно. З PostgreSQL це можна зробити за допомогою індексу. Ні MySQL, ні MariaDB наразі не підтримують KNN

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.