SQL:根据lat/lon寻找最近的邻居
SQL: Finding closest neighbor based on lat/lon
我有一个 table 包含 lat/lon 家咖啡馆。我想进行 SQL 查询,以提供离每家咖啡馆最近的咖啡馆。有人可以提供有关如何执行此操作的建议吗?
table基本上是这样的:
咖啡馆 ID
+-------------------------+----------------------+----------------------+
| cafe_id | gps_latitude | gps_longitude |
+-------------------------+----------------------+----------------------+
| 011-1003 | 55.86649500000000000 | 8.16856200000000000 |
| 192-143 | 57.04419159749860000 | 10.36447024359820000 |
| 037-0233 | 55.08773849210000000 | 8.56101036070000000 |
| 121-934 | 56.89120900000000000 | 9.16818100000000000 |
+-------------------------+----------------------+----------------------+
非常感谢任何帮助!
试试这个:
SELECT zip, primary_city, latitude, longitude,
111.045* DEGREES(ACOS(COS(RADIANS(latpoint))
* COS(RADIANS(latitude))
* COS(RADIANS(longpoint) - RADIANS(longitude))
+ SIN(RADIANS(latpoint))
* SIN(RADIANS(latitude)))) AS distance_in_km
FROM zip
JOIN (
SELECT 42.81 AS latpoint, -70.81 AS longpoint
) AS p ON 1=1
ORDER BY distance_in_km;
可以在此处阅读有关此主题的更多信息:
http://www.plumislandmedia.net/mysql/haversine-mysql-nearest-loc/
下面的查询有助于找出两个(邻居)最近的位置。
在 MySQL 中使用当前经纬度值设置变量
SET @lat=55.866495, @lng=8.168562;
执行查询
(SELECT tbl.cafe_id , tbl.gps_latitude, tbl.gps_longitude, 111.045 * DEGREES(ACOS(COS(RADIANS(@lat))
* COS(RADIANS(gps_latitude))
* COS(RADIANS(gps_longitude) - RADIANS(@lng))
+ SIN(RADIANS(@lat))
* SIN(RADIANS(gps_latitude))))
AS distance_in_km
FROM table_name as tbl ORDER BY distance_in_km ASC LIMIT 0,1)
UNION ALL
(SELECT tbl.cafe_id , tbl.gps_latitude, tbl.gps_longitude, 111.045 * DEGREES(ACOS(COS(RADIANS(@lat))
* COS(RADIANS(gps_latitude))
* COS(RADIANS(gps_longitude) - RADIANS(@lng))
+ SIN(RADIANS(@lat))
* SIN(RADIANS(gps_latitude))))
AS distance_in_km
FROM table_name as tbl ORDER BY distance_in_km DESC LIMIT 0,1);
您可以使用 Spherical Law of Cosines to get the distance expressed in earth-radii。
有些人更喜欢Haversine formula,因为它提供更好的精度,但考虑到MySql的浮点精度足够高,两者之间的差异可以忽略不计。第一个实现起来比较简单:
select c1.cafe_id,
substring_index (
group_concat( c2.cafe_id order by
acos( sin(radians(c1.gps_latitude)) * sin(radians(c2.gps_latitude))
+ cos(radians(c1.gps_latitude)) * cos(radians(c2.gps_latitude))
* cos(radians(c2.gps_longitude-c1.gps_longitude)) ) ),
',', 1) nearest
from cafe c1
inner join cafe c2 on c1.cafe_id <> c2.cafe_id
group by c1.cafe_id
样本数据的输出是:
| cafe_id | nearest |
|----------|----------|
| 011-1003 | 037-0233 |
| 037-0233 | 011-1003 |
| 121-934 | 192-143 |
| 192-143 | 121-934 |
这是一个MySql fiddle.
说明
在group_concat
聚合函数的order by
子句中使用距离计算,得到cafe_id[=46的逗号分隔列表=] 值按照与您分组的咖啡馆的距离排序。 substring_index
函数从该列表中提取第一项。
连接条件很重要,因为没有它,您会将咖啡馆本身作为最近的邻居(那么它的距离显然为 0)。
设置距离限制
在评论中,您要求仅包含特定半径内的邻居。
在这种情况下,您可以输出 "distance",转换为公里(英里将是一个不同的因素):
select c1.cafe_id,
substring_index (
group_concat( c2.cafe_id order by
acos( sin(radians(c1.gps_latitude)) * sin(radians(c2.gps_latitude))
+ cos(radians(c1.gps_latitude)) * cos(radians(c2.gps_latitude))
* cos(radians(c2.gps_longitude-c1.gps_longitude)) ) ),
',', 1) nearest,
min(
acos( sin(radians(c1.gps_latitude)) * sin(radians(c2.gps_latitude))
+ cos(radians(c1.gps_latitude)) * cos(radians(c2.gps_latitude))
* cos(radians(c2.gps_longitude-c1.gps_longitude)) ) )
* 6371 km
from cafe c1
inner join cafe c2 on c1.cafe_id <> c2.cafe_id
group by c1.cafe_id
现在您可以根据距离决定是否要忽略邻居。如果你真的想排除最近邻居太远的咖啡馆,那么在末尾添加一个 having
子句:
having km < 5
如果您更喜欢里程,则在 SQL 中使用 3959 作为乘数而不是 6371。
我有一个 table 包含 lat/lon 家咖啡馆。我想进行 SQL 查询,以提供离每家咖啡馆最近的咖啡馆。有人可以提供有关如何执行此操作的建议吗?
table基本上是这样的:
咖啡馆 ID
+-------------------------+----------------------+----------------------+
| cafe_id | gps_latitude | gps_longitude |
+-------------------------+----------------------+----------------------+
| 011-1003 | 55.86649500000000000 | 8.16856200000000000 |
| 192-143 | 57.04419159749860000 | 10.36447024359820000 |
| 037-0233 | 55.08773849210000000 | 8.56101036070000000 |
| 121-934 | 56.89120900000000000 | 9.16818100000000000 |
+-------------------------+----------------------+----------------------+
非常感谢任何帮助!
试试这个:
SELECT zip, primary_city, latitude, longitude,
111.045* DEGREES(ACOS(COS(RADIANS(latpoint))
* COS(RADIANS(latitude))
* COS(RADIANS(longpoint) - RADIANS(longitude))
+ SIN(RADIANS(latpoint))
* SIN(RADIANS(latitude)))) AS distance_in_km
FROM zip
JOIN (
SELECT 42.81 AS latpoint, -70.81 AS longpoint
) AS p ON 1=1
ORDER BY distance_in_km;
可以在此处阅读有关此主题的更多信息: http://www.plumislandmedia.net/mysql/haversine-mysql-nearest-loc/
下面的查询有助于找出两个(邻居)最近的位置。
在 MySQL 中使用当前经纬度值设置变量
SET @lat=55.866495, @lng=8.168562;
执行查询
(SELECT tbl.cafe_id , tbl.gps_latitude, tbl.gps_longitude, 111.045 * DEGREES(ACOS(COS(RADIANS(@lat))
* COS(RADIANS(gps_latitude))
* COS(RADIANS(gps_longitude) - RADIANS(@lng))
+ SIN(RADIANS(@lat))
* SIN(RADIANS(gps_latitude))))
AS distance_in_km
FROM table_name as tbl ORDER BY distance_in_km ASC LIMIT 0,1)
UNION ALL
(SELECT tbl.cafe_id , tbl.gps_latitude, tbl.gps_longitude, 111.045 * DEGREES(ACOS(COS(RADIANS(@lat))
* COS(RADIANS(gps_latitude))
* COS(RADIANS(gps_longitude) - RADIANS(@lng))
+ SIN(RADIANS(@lat))
* SIN(RADIANS(gps_latitude))))
AS distance_in_km
FROM table_name as tbl ORDER BY distance_in_km DESC LIMIT 0,1);
您可以使用 Spherical Law of Cosines to get the distance expressed in earth-radii。
有些人更喜欢Haversine formula,因为它提供更好的精度,但考虑到MySql的浮点精度足够高,两者之间的差异可以忽略不计。第一个实现起来比较简单:
select c1.cafe_id,
substring_index (
group_concat( c2.cafe_id order by
acos( sin(radians(c1.gps_latitude)) * sin(radians(c2.gps_latitude))
+ cos(radians(c1.gps_latitude)) * cos(radians(c2.gps_latitude))
* cos(radians(c2.gps_longitude-c1.gps_longitude)) ) ),
',', 1) nearest
from cafe c1
inner join cafe c2 on c1.cafe_id <> c2.cafe_id
group by c1.cafe_id
样本数据的输出是:
| cafe_id | nearest |
|----------|----------|
| 011-1003 | 037-0233 |
| 037-0233 | 011-1003 |
| 121-934 | 192-143 |
| 192-143 | 121-934 |
这是一个MySql fiddle.
说明
在group_concat
聚合函数的order by
子句中使用距离计算,得到cafe_id[=46的逗号分隔列表=] 值按照与您分组的咖啡馆的距离排序。 substring_index
函数从该列表中提取第一项。
连接条件很重要,因为没有它,您会将咖啡馆本身作为最近的邻居(那么它的距离显然为 0)。
设置距离限制
在评论中,您要求仅包含特定半径内的邻居。
在这种情况下,您可以输出 "distance",转换为公里(英里将是一个不同的因素):
select c1.cafe_id,
substring_index (
group_concat( c2.cafe_id order by
acos( sin(radians(c1.gps_latitude)) * sin(radians(c2.gps_latitude))
+ cos(radians(c1.gps_latitude)) * cos(radians(c2.gps_latitude))
* cos(radians(c2.gps_longitude-c1.gps_longitude)) ) ),
',', 1) nearest,
min(
acos( sin(radians(c1.gps_latitude)) * sin(radians(c2.gps_latitude))
+ cos(radians(c1.gps_latitude)) * cos(radians(c2.gps_latitude))
* cos(radians(c2.gps_longitude-c1.gps_longitude)) ) )
* 6371 km
from cafe c1
inner join cafe c2 on c1.cafe_id <> c2.cafe_id
group by c1.cafe_id
现在您可以根据距离决定是否要忽略邻居。如果你真的想排除最近邻居太远的咖啡馆,那么在末尾添加一个 having
子句:
having km < 5
如果您更喜欢里程,则在 SQL 中使用 3959 作为乘数而不是 6371。