我目前在mysql数据库中有不到一百万个位置,都有经度和纬度信息。
我试图通过查询找到一个点和许多其他点之间的距离。它没有我想要的那么快,尤其是每秒100+次。
有没有比mysql更快的查询或更快的系统?我使用这个查询:
SELECT
name,
( 3959 * acos( cos( radians(42.290763) ) * cos( radians( locations.lat ) )
* cos( radians(locations.lng) - radians(-71.35368)) + sin(radians(42.290763))
* sin( radians(locations.lat)))) AS distance
FROM locations
WHERE active = 1
HAVING distance < 10
ORDER BY distance;
注:提供的距离单位为英里。如果需要公里,请使用6371而不是3959。
使用mysql
SET @orig_lon = 1.027125;
SET @dest_lon = 1.027125;
SET @orig_lat = 2.398441;
SET @dest_lat = 2.398441;
SET @kmormiles = 6371;-- for distance in miles set to : 3956
SELECT @kmormiles * ACOS(LEAST(COS(RADIANS(@orig_lat)) *
COS(RADIANS(@dest_lat)) * COS(RADIANS(@orig_lon - @dest_lon)) +
SIN(RADIANS(@orig_lat)) * SIN(RADIANS(@dest_lat)),1.0)) as distance;
参见:https://andrew.hedges.name/experiments/haversine/
参见:https://stackoverflow.com/a/24372831/5155484
参见:http://www.plumislandmedia.net/mysql/haversine-mysql-nearest-loc/
注意:LEAST用于避免null值,如https://stackoverflow.com/a/24372831/5155484上建议的注释
一个MySQL函数,返回两个坐标之间的米数:
CREATE FUNCTION DISTANCE_BETWEEN (lat1 DOUBLE, lon1 DOUBLE, lat2 DOUBLE, lon2 DOUBLE)
RETURNS DOUBLE DETERMINISTIC
RETURN ACOS( SIN(lat1*PI()/180)*SIN(lat2*PI()/180) + COS(lat1*PI()/180)*COS(lat2*PI()/180)*COS(lon2*PI()/180-lon1*PI()/180) ) * 6371000
要以不同的格式返回值,请将函数中的6371000替换为您选择的单位中的地球半径。例如,公里是6371,英里是3959。
要使用该函数,只需像调用MySQL中的任何其他函数一样调用它。例如,如果你有一个表格城市,你可以找到每个城市与其他城市之间的距离:
SELECT
`city1`.`name`,
`city2`.`name`,
ROUND(DISTANCE_BETWEEN(`city1`.`latitude`, `city1`.`longitude`, `city2`.`latitude`, `city2`.`longitude`)) AS `distance`
FROM
`city` AS `city1`
JOIN
`city` AS `city2`
我真的很喜欢@Māris kiseovs的解决方案,但我喜欢许多其他人可能会从他的例子中得到Lat和lng的POINTS。在概括它时,我想我会分享它。在我的情况下,我需要找到所有的开始点,在end_point的一定半径内。
我希望这能帮助到一些人。
SELECT @LAT := ST_X(end_point), @LNG := ST_Y(end_point) FROM routes WHERE route_ID = 280;
SELECT
*,
(6371e3 * ACOS(COS(RADIANS(@LAT)) * COS(RADIANS(ST_X(start_point)))
* COS(RADIANS(ST_Y(start_point)) - RADIANS(@LNG)) + SIN(RADIANS(@LAT))
* SIN(RADIANS(ST_X(start_point))))) AS distance
FROM routes
WHERE MBRContains
(
LineString
(
Point (
@LNG + 15 / (111.320 * COS(RADIANS(@LAT))),
@LAT + 15 / 111.133
),
Point (
@LNG - 15 / (111.320 * COS(RADIANS(@LAT))),
@LAT - 15 / 111.133
)
),
POINT(ST_Y(end_point),ST_X(end_point))
)
HAVING distance < 100
ORDER By distance;
下面的MySQL函数发布在这篇博文上。我还没有对它进行太多测试,但从我从帖子中收集到的内容来看,如果你的纬度和经度字段被索引了,这可能对你很有用:
DELIMITER $$
DROP FUNCTION IF EXISTS `get_distance_in_miles_between_geo_locations` $$
CREATE FUNCTION get_distance_in_miles_between_geo_locations(
geo1_latitude decimal(10,6), geo1_longitude decimal(10,6),
geo2_latitude decimal(10,6), geo2_longitude decimal(10,6))
returns decimal(10,3) DETERMINISTIC
BEGIN
return ((ACOS(SIN(geo1_latitude * PI() / 180) * SIN(geo2_latitude * PI() / 180)
+ COS(geo1_latitude * PI() / 180) * COS(geo2_latitude * PI() / 180)
* COS((geo1_longitude - geo2_longitude) * PI() / 180)) * 180 / PI())
* 60 * 1.1515);
END $$
DELIMITER ;
示例用法:
假设有一个名为places的表,其中包含纬度和经度字段:
SELECT get_distance_in_miles_between_geo_locations(-34.017330, 22.809500,
AS distance_from_input FROM places;