MySQL中给定半径内的查询点

gjb*_*gjb 9 mysql database gis spatial mysql-spatial

我创建了以下MySQL表来存储纬度/经度坐标以及每个点的名称:

CREATE TABLE `points` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(128) NOT NULL,
  `location` point NOT NULL,
  PRIMARY KEY (`id`),
  SPATIAL KEY `location` (`location`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1;
Run Code Online (Sandbox Code Playgroud)

我试图查询:

  • 给定点n英里范围内的所有点;
  • 每个返回点距给定点的距离

我发现的所有示例都涉及使用最小边界矩形(MBR)而不是半径.该表包含大约100万个点,因此需要尽可能高效.

src*_*der 6

对于 MySQL 5.7+

鉴于我们有以下简单的表格,

create table example (
  id bigint not null auto_increment primary key,
  lnglat point not null
);

create spatial index example_lnglat 
    on example (lnglat);
Run Code Online (Sandbox Code Playgroud)

有了以下简单的数据,

insert into example (lnglat) 
values
(point(-2.990435, 53.409246)),
(point(-2.990037, 53.409471)),
(point(-2.989736, 53.409676)),
(point(-2.989554, 53.409797)),
(point(-2.989350, 53.409906)),
(point(-2.989178, 53.410085)),
(point(-2.988739, 53.410309)),
(point(-2.985874, 53.412656)),
(point(-2.758019, 53.635928));
Run Code Online (Sandbox Code Playgroud)

您将使用以下 st 函数组合获得另一个点的给定范围内的点(注意:我们必须在多边形内搜索):

set @px = -2.990497;
set @py = 53.410943;
set @range = 150; -- meters
set @rangeKm = @range / 1000;

set @search_area = st_makeEnvelope (
  point((@px + @rangeKm / 111), (@py + @rangeKm / 111)),
  point((@px - @rangeKm / 111), (@py - @rangeKm / 111))
);

select id, 
       st_x(lnglat) lng, 
       st_y(lnglat) lat,
       st_distance_sphere(point(@px, @py), lnglat) as distance
  from example
 where st_contains(@search_area, lnglat);
Run Code Online (Sandbox Code Playgroud)

结果应该是这样的:

3   -2.989736   53.409676   149.64084252776277
4   -2.989554   53.409797   141.93232714661812
5   -2.98935    53.409906   138.11516275402533
6   -2.989178   53.410085   129.40289289527473
Run Code Online (Sandbox Code Playgroud)

对于距离参考,如果我们移除约束,测试点的结果如下所示:

1   -2.990435   53.409246   188.7421181457556
2   -2.990037   53.409471   166.49406509160158
3   -2.989736   53.409676   149.64084252776277
4   -2.989554   53.409797   141.93232714661812
5   -2.98935    53.409906   138.11516275402533
6   -2.989178   53.410085   129.40289289527473
7   -2.988739   53.410309   136.1875540498202
8   -2.985874   53.412656   360.78532732013963
9   -2.758019   53.635928   29360.27797292756
Run Code Online (Sandbox Code Playgroud)

注 1:该字段称为 lnglat,因为如果您将点视为 (x, y) 则这是正确的顺序,并且也是大多数函数(如点)接受参数的顺序

注意 2:如果您要使用圆圈,则实际上无法利用空间索引;还要注意,点字段可以设置为接受空值,但如果它可以为空(索引中的所有字段都要求为非空值),则空间索引不能对其进行索引。

注 3:st_buffer 被认为(根据文档)对于这个用例是不利的

注 4:上述函数(特别是 st_distance_sphere)被记录为快速但不一定超准确;如果您的数据对此非常敏感,则为搜索增加一点回旋余地并对结果集进行一些微调


Luk*_*ský 2

半径不能有效地转位。您应该使用边界矩形来快速获取您可能正在寻找的点,然后过滤半径之外的点。

  • 不,这是一个普遍问题。PostgreSQL 确实让你变得更容易,因为你可以明确地询问该点是否包含在一个圆中,这将尽可能地使用索引,但我相信它也只会首先使用矩形搜索。我看不到任何 MySQL 函数可以做到这一点,但你可以简单地计算中心和点之间的距离。 (2认同)