按计算距离对 MySQL 结果排序(距离未存储在 DB 中)

Posted

技术标签:

【中文标题】按计算距离对 MySQL 结果排序(距离未存储在 DB 中)【英文标题】:Sorting MySQL results by calculated distance (distance not stored in DB) 【发布时间】:2015-09-13 22:19:17 【问题描述】:

我将“地点”存储在数据库中,我正在使用 php 来访问它们。我想要做的是返回所有按相对于某个地方的距离排序的地方。

这个地点将是来自 android 应用程序的动态,即我想显示最接近用户位置的所有地点。

最好的方法是什么?将PHP中的所有位置检索到一个数组中,计算每个位置的距离,然后按距离对该数组进行排序是否有效/高效?或者有没有更简单/更快的方法来完成我的需要?

谢谢!

【问题讨论】:

【参考方案1】:

您可以在 SQL 中通过即时计算距离来完成这一切。这是存储的lat/lon 字段与提供的$lat/$lon 之间距离的粗略近似值。

$dlat = "(`lat`-$lat)";
$dlon = "(`lon`-$lon)*".cos($lat*3.1415/180);
$dist_sql = "$dlat*$dlat+$dlon*$dlon";
$sql = "IF(`lat` IS NULL,1e20,$dist_sql)";

然后像使用任何其他字段一样使用$sql,例如

SELECT * FROM `table` ORDER BY $sql ASC

这远非完美,但至少可以帮助您入门。

cos() 之所以出现,是因为随着(绝对)纬度的增加,经度的一个度数会缩短距离。

要从$sql 得到以公里为单位的实际值,除以 90 并乘以 10000 公里。 (再次:非常粗略的近似值!)

【讨论】:

太棒了!谢谢,不知道这可以在 SQL 中完成。我希望有更好的方法。谢谢你的开始,我会努力的。【参考方案2】:

如果数据库中的每个位置都有纬度/经度字段,则在数据库中进行距离计算。以下存储过程需要进行一些调整以适合您的数据库,因为我们不知道架构 - 但它假定一个名为 places 的表具有一个包含位置纬度的字段和一个包含经度的字段。

CREATE PROCEDURE `spNearestPlaces`(IN `_latitude` double, IN `_longitude` double, IN `_radius` INT)
    LANGUAGE SQL
    NOT DETERMINISTIC
    CONTAINS SQL
    SQL SECURITY DEFINER
    COMMENT ''
proc:begin
    declare strsql varchar(10000);
    declare lat double default 0;
    declare lng double default 0;
    declare radius float default 0;
    declare earth_radius integer default 0;
    declare lon1 float;
    declare lat1 float;
    declare lon2 float;
    declare lat2 float;



    set @lat=_latitude;
    set @lng=_longitude;

    set @radius=cast(_radius as unsigned);
    set @earth_radius=3956;

    set @lon1 = @lng - @radius/ceil( cos( radians( @lat ) ) * 69 );
    set @lon2 = @lng + @radius/ceil( cos( radians( @lat ) ) * 69 );
    set @lat1 = @lat - ( @radius/69 );
    set @lat2 = @lat + ( @radius/69 );




    set @strsql=concat("select
            p.`id`,
            p.`latitude`,
            p.`longitude`,
            ROUND( @earth_radius * 2 * ASIN( SQRT( POWER( SIN( (@lat - p.`latitude`) * pi()/180 / 2), 2) +COS(@lat * pi()/180) * COS(p.`latitude` * pi()/180) *POWER(SIN((@lng - p.`longitude`) * pi()/180 / 2), 2) ) ),2) AS 'distance'
            from `places` p
            where   
                    p.`latitude` between @lat1 and @lat2
                    and 
                    p.`longitude` between @lon1 and @lon2
            having `distance` <= @radius 
            order by `distance`;");

    prepare stmt from @strsql;
    execute stmt;
    deallocate prepare stmt;
end

你会从 php 调用存储过程,例如:-

$lat=56.564;
$lng=-2.5465;
$radius=5;

call `spNearestPlaces`($lat,$lng,$radius);

【讨论】:

太棒了,谢谢,我会检查一下。截至目前,我的桌子没有纬度/经度,但如果需要,我可以轻松添加这些

以上是关于按计算距离对 MySQL 结果排序(距离未存储在 DB 中)的主要内容,如果未能解决你的问题,请参考以下文章

mysql 下 计算 两点 经纬度 之间的距离 计算结果排序

Mysql 之根据经纬度按距离排序

在 Swift 中按计算的距离对数组进行排序

在 ONGR ElasticsearchDSL 中按距离对结果进行排序

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

按邮政编码距离升序计算和排序用户的最快方式(也是最优化的)