如何有效地找到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           

有160万个biz。当然,计算所有这些对象的距离然后对其进行排序是愚蠢的。

那就是地理空间索引正确的地方?

那么我需要转换什么SQL逗号?

注意:

  1. 我正在使用mysql myisam空间索引。但是我之前没有指定。因此,我将接受回答该问题的人表示感谢,并提出另一个问题。
  2. 我不想计算整个桌子的距离
  3. 我不想为仍然效率低下的任何区域计算距离
  4. 我确实想计算合理数量的点的距离,因为我想按距离对点进行排序,并能够显示点1-20、21-40、41-60等。

3
交叉发布dba.stackexchange.com/questions/19595/…(似乎也很难回答每个答案都针对PostGIS的问题)
Evan Carroll,

Answers:


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的本机功能)。它将快几个数量级。

该博客文章的摘录描述了如何在不使用PostGIS的情况下使用knn gist

$ 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 2012年

因此+1,但我无法选择此答案。我应该再提出一个问题吗?
user4951 2012年

@JimThio MySQL没有最近的邻居索引,因此在出现最近的邻居查询(带有ORDER BY ST_Distance的ST_Dwithin)之前,您将不得不依靠类似于PostGIS的方法。欢迎回到中世纪:)
Ragi Yaser Burhum 2012年

所以我得去mongodb吗?让我猜猜。如果您甚至无法做最简单的事情(例如查找20个最近的点),那么在mysql上具有空间索引的意义何在?
user4951 2012年

1
您可以使用窗口找到最近的点。@lynxlynxlynx描述的任何其他空间数据库也是如此。您可以通过将窗口乘以2来不断增加窗口。是的,Mongo或任何其他数据库也是如此。关键是您减少了大多数其他功能。此外,每个人都知道直到最近,MySQL从来都不是任何空间方面的认真竞争者。
Ragi Yaser Burhum 2012年

8

在PostgreSQL 9.1上的PostGIS 2.0中,可以使用KNN索引的最近邻居运算符,例如:

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

以上内容应在几毫秒内查询。

对于20的下一个倍数,修改到OFFSET 20OFFSET 40等...


我能知道什么意思<->吗?谢谢。
northtree

<->是返回2D距离的运算符。
迈克T

1

MySQL空间

这里的每个人都在告诉您如何使用KNN使用PostgreSQL进行操作,而没有告诉您优点。使用MySQL,如果不计算所有邻居的距离,就无法确定最近的邻居。那太慢了。使用PostgreSQL可以在索引上完成。MySQL和MariaDB目前都不支持KNN

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.