有效地从数据库中选择最近(距离)记录

时间:2011-03-07 09:16:21

标签: mysql math distance

我有一个拥有4万个场地的数据库,现在正在增长。

假设我是红点

Easy
我希望能够尽快找到最接近的记录。

然而,下一个项目的距离也可能是任何东西。并且也可能有0-n匹配。但是,当我只是寻找1时,我是否需要加载所有40000结果? Less obvious

如何按距离对记录进行排序?它应该在MYSQL还是PHP中完成? 这种计算几乎发生在每个用户,每页的每个请求上,因此解决方案需要快速。

编辑感谢快速而有前途的答案,我需要查看这些资源,并在几天内接受/评论答案。

4 个答案:

答案 0 :(得分:8)

这个Scribd演示文稿中包含了这个问题(理论+数学公式+ Mysql): Geo Distance with MySQL

我希望它涵盖你需要的一切

答案 1 :(得分:3)

最简单的解决方案是简单地计算每条记录的距离并按此值排序。问题是:这非常昂贵且您不能使用索引。您可以通过仅查看记录的子集来降低成本,可能会受到边界框的限制,如此处的海报所示。

如果您想要一个清晰而快速的解决方案,请查看MySQL的 Spatial Extensions 。这些完全是为了你想做的。这些支持:

  • 新的列类型'点'
  • 针对距离查询优化的特殊索引类型
  • 距离操作员。

This howto提供了一些示例:

CREATE TABLE address (
  address CHAR(80) NOT NULL,
  address_loc POINT NOT NULL,
  PRIMARY KEY(address),
  SPATIAL KEY(address_loc)
);
CREATE TABLE cab (
  cab_id INT AUTO_INCREMENT NOT NULL,
  cab_driver CHAR(80) NOT NULL,
  cab_loc POINT NOT NULL,
  PRIMARY KEY(cab_id),
  SPATIAL KEY(cab_loc)
);

SELECT
  c.cab_driver,
  ROUND(GLength(LineStringFromWKB(LineString(AsBinary(c.cab_loc),
                                             AsBinary(a.address_loc)))))
    AS distance
FROM cab c, address a
WHERE a.address = 'Foobar street 110'
ORDER BY distance ASC LIMIT 1;

答案 2 :(得分:1)

创建一个“边界框”以在SQL查询的WHERE子句中使用,如本article on Movable Type中所述(使用PHP代码示例),然后在查询中包含Haversine公式以计算实际距离,以及按距离ASC排序结果。最近的场地将是结果集中的第一个回归。

这是帮助您提高性能的边界框,因为这意味着您只需对数据的一小部分进行昂贵的距离计算

如果初始查询未返回任何记录,请加宽边界框,然后再次执行查询,直到获得响应为止。

答案 3 :(得分:1)

除了通过反复试验之外,找不到距离是没有效的方法。也就是说,使用MySQL,您无法按目标距离对记录进行排名,然后选择最顶层的记录。最好的方法是选择您认为最接近的记录将在其中的距离。太大的数字,你会得到太多的记录,太小的数字,你不会得到任何。假设你选择40个单位。

WHERE xcoord BETWEEN n - 40 AND n + 40 AND ycoord BETWEEN n - 40 AND n + 40

现在你已经在80 x 80的盒子里面得到了所有的坐标记录,以你的目标为中心(如果你在纬度和经度上工作,那个盒子会有点倾斜,但那不是'真的很重要)。现在使用Haversine方程式(如果你正在处理纬度和经度)或者毕达哥拉斯(Pythagoras)(如果它只是笛卡尔坐标)来计算目标与每个点之间的距离。