我必须查询包含数千个条目的数据库,并根据距指定点的距离对其进行排序。
问题是每个条目都有纬度和经度,我需要检索每个条目来计算它的距离。对于大型数据库,我不想检索每一行,这可能需要一些时间。
有什么方法可以将其构建到 mysql 查询中,以便我只需要检索最近的 15 个条目。
例如
`SELECT events.id, caclDistance($latlng, events.location) AS distance FROM events ORDER BY distance LIMIT 0,15`
function caclDistance($old, $new){
//Calculates the distance between $old and $new
}
最佳答案
选项 1: 通过切换到支持 GeoIP 的数据库在数据库上进行计算。
选项 2: 使用这样的存储过程对数据库进行计算:
CREATE FUNCTION calcDistance (latA double, lonA double, latB double, LonB double)
RETURNS double DETERMINISTIC
BEGIN
SET @RlatA = radians(latA);
SET @RlonA = radians(lonA);
SET @RlatB = radians(latB);
SET @RlonB = radians(LonB);
SET @deltaLat = @RlatA - @RlatB;
SET @deltaLon = @RlonA - @RlonB;
SET @d = SIN(@deltaLat/2) * SIN(@deltaLat/2) +
COS(@RlatA) * COS(@RlatB) * SIN(@deltaLon/2)*SIN(@deltaLon/2);
RETURN 2 * ASIN(SQRT(@d)) * 6371.01;
END//
如果您的数据库中有经纬度索引,您可以通过在 PHP 中计算初始边界框($minLat、$maxLat、$minLong 和 $maxLong)来减少需要计算的次数,并基于此将行限制为条目的子集(WHERE latitude BETWEEN $minLat AND $maxLat AND longitude BETWEEN $minLong AND $maxLong)。然后MySQL只需要对该行的子集执行距离计算。
如果您只是简单地使用存储过程来计算距离)那么 SQL 仍然必须查看数据库中的每条记录,并计算数据库中每条记录的距离,然后才能决定是否返回该行或丢弃它。
因为计算执行起来比较慢,如果你能减少需要计算的行集,消除明显落在所需距离之外的行,那会更好,这样我们就只执行较少行数的昂贵计算。
如果你认为你所做的基本上是在 map 上画一个圆,以你的初始点为中心,以距离为半径;然后公式简单地识别出哪些行落在该圆圈内......但它仍然必须检查每一行。
使用边界框就像先在 map 上绘制一个正方形,其左、右、上、下边缘与中心点的距离适当。然后我们的圆将在该框内绘制,圆上的最北端、最东端、最南端和最西端接触框的边界。有些行会落在该框之外,因此 SQL 甚至不会费心尝试计算这些行的距离。它只计算落在边界框内的那些行的距离,以查看它们是否也落在圆圈内。
在您的 PHP 中(猜测您正在从 $ 变量名运行 PHP),我们可以使用一个非常简单的计算,根据我们的距离计算出最小和最大纬度和经度,然后在 WHERE 子句中设置这些值你的 SQL 语句。这实际上是我们的盒子,落在盒子外面的任何东西都会自动丢弃,无需实际计算其距离。
在 Movable Type website 上对此有很好的解释(使用 PHP 代码)对于计划使用 PHP 进行任何地理定位工作的任何人来说,这应该是必不可少的阅读 Material 。
编辑 calcDistance 存储过程中的值 6371.01 是为您提供以公里为单位的返回结果的乘数。如果您想以英里、海里、米等为单位计算结果,请使用适当的替代乘数
关于php - mysql查询中的距离计算,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3986556/