使用空间分析函数和数据类型在MySQL中按距离排序

Lor*_*ssi 7 php mysql geospatial geo laravel-eloquent

我正在使用Laravel 5.5构建一个php web应用程序,我需要显示按用户指定位置的距离排序的地点列表(例如商店).这些地方将存储在MySQL数据库中,并应作为Eloquent ORM模型实例进行检索.

做了一些研究我发现了很多关于这个主题的帖子和问题(提出了不同的解决方案),但是,由于对数据库和地理定位/地理空间分析的经验很少,他们大多困惑我,我想知道要采用什么方法和什么是这种情况下的最佳做法.

我读过的大多数答案都建议在SQL查询中使用半字形公式余弦球面定律,这看起来像(从这个答案中取得的例子):

$sf = 3.14159 / 180; // scaling factor
$sql = "SELECT * FROM table 
    WHERE lon BETWEEN '$minLon' AND '$maxLon' 
      AND lat BETWEEN '$minLat' AND '$maxLat'
    ORDER BY ACOS(SIN(lat*$sf)*SIN($lat*$sf) + COS(lat*$sf)*COS($lat*$sf)*COS((lon-$lon)*$sf))";
Run Code Online (Sandbox Code Playgroud)

这篇文章指出,在短距离内,假设地球平坦并计算一个简单的欧氏距离是一个很好的近似,并且比使用半正式公式更快.
由于我一次只需要对一个城市内的地点进行排序,这似乎是一个很好的解决方案.

但是,大多数这些帖子和SO答案都有几年的历史了,我想知道现在(MySQL 5.7)是否有更好的解决方案.

例如,这些都不岗位使用任何的MySQL的"空间分析功能",喜欢ST_Distance_SphereST_Distance这似乎是完全用于这一目的.
是否有任何理由(例如,性能,精度)使用这些函数而不是在查询中编写公式?(我不知道这些函数内部使用了哪种算法)

我也不知道应该如何存储每个地方的坐标.大多数的我看到假定坐标的实例将被存储在单独的lat,lon列作为双打或FLOAT(10,6)(如在该示例中由谷歌),但也MySQL的POINT数据类型似乎是适当用于存储地理坐标.
这两种方法的优点和缺点是什么?

如何使用索引来加速这些查询?例如,我读过"空间索引",但我认为它们只能用于限制结果MBRContains(),而不是按距离实际排序结果.

那么,我应该如何存储地点的坐标以及如何查询它们按距离排序?

Ric*_*mes 5

除了ST_Distance_Sphere之外,5.7不会为表带来任何额外的内容.(SPATIAL已经实施.)

对于"数千"点,您拥有的代码可能是最好的.包括

INDEX(lat, lng),
INDEX(lng, lat)
Run Code Online (Sandbox Code Playgroud)

除非你伸展数千英里(公里),否则我不会担心地球的曲率.即便如此,代码和该功能应该足够好.

不要使用FLOAT(m,n),仅限使用FLOAT.下面的链接给出了可用的精度FLOAT和其他表示.

如果你有这么多点,你无法完全缓存表及其索引(数百万点),你可以使用,它使用一些技巧来避免像上述解决方案那样冗长的扫描.由于PARTITION限制,lat/lng表示为缩放整数.(但这很容易在输入/输出中进行转换.)地球的曲率,极点和日期线都被处理掉了.