Mysql 查找两个横向/纵向点之间距离的最快方法
我目前在mysql数据库中有不到一百万个位置,都有经度和纬度信息 我试图通过查询找到一个点和许多其他点之间的距离。它没有我想要的那么快,尤其是每秒100+点击 有没有比mysql更快的查询或可能更快的系统?我正在使用此查询:Mysql 查找两个横向/纵向点之间距离的最快方法,mysql,location,gis,Mysql,Location,Gis,我目前在mysql数据库中有不到一百万个位置,都有经度和纬度信息 我试图通过查询找到一个点和许多其他点之间的距离。它没有我想要的那么快,尤其是每秒100+点击 有没有比mysql更快的查询或可能更快的系统?我正在使用此查询: SELECT name, ( 3959 * acos( cos( radians(42.290763) ) * cos( radians( locations.lat ) ) * cos( radians(locations.lng) - radian
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;
选择
名称
(3959*acos(弧度(42.290763))*cos(弧度(位置.lat))
*cos(弧度(locations.lng)-弧度(-71.35368))+sin(弧度(42.290763))
*sin(弧度(locations.lat)))作为距离
从地点
其中活动=1
距离小于10的
按距离排序;
注意:提供的距离以英里为单位。如果您需要公里数,请使用6371
而不是3959
- 使用
表中MyISAM
数据类型的Geometry
值创建点Point
- 在这些点上创建
索引SPATIAL
- 使用
查找值:mbracontains()
SELECT * FROM table WHERE MBRContains(LineFromText(CONCAT( '(' , @lon + 10 / ( 111.1 / cos(RADIANS(@lon))) , ' ' , @lat + 10 / 111.1 , ',' , @lon - 10 / ( 111.1 / cos(RADIANS(@lat))) , ' ' , @lat - 10 / 111.1 , ')' ) ,mypoint)
SELECT *
FROM table
WHERE MBRContains
(
LineString
(
Point (
@lon + 10 / ( 111.1 / COS(RADIANS(@lat))),
@lat + 10 / 111.1
),
Point (
@lon - 10 / ( 111.1 / COS(RADIANS(@lat))),
@lat - 10 / 111.1
)
),
mypoint
)
这将选择框内大约所有的点(@lat+/-10公里,@lon+/-10公里)
这实际上不是一个长方体,而是一个球形矩形:球体的纬度和经度边界段。这可能不同于弗兰兹·约瑟夫土地上的一个普通矩形,但在大多数有人居住的地方,它与之非常接近
- 应用附加过滤以选择圆(而不是正方形)内的所有内容
- 可能会应用额外的精细过滤来说明大圆距离(对于大距离)
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
and locations.lat between X1 and X2
and locations.Long between y1 and y2
having distance < 10 ORDER BY distance;
选择名称,
(3959*acos(弧度(42.290763))
*cos(弧度(locations.lat))
*cos(弧度(locations.lng)-弧度(-71.35368))
+正弦(弧度(42.290763))
*sin(弧度(locations.lat)))作为距离
从地点
其中活动=1
以及X1和X2之间的位置.lat
和位置。长在y1和y2之间
距离小于10个数量级的距离;
我知道使用MS SQL我可以构建一个SQL语句,声明四个浮点(X1、Y1、X2、Y2),并在“main”select语句之前计算它们,就像我说的,我不知道MySql是否可以做到这一点。然而,我仍然倾向于在C#中构建这四个点,并将它们作为参数传递给SQL查询
很抱歉,我帮不上什么忙,如果有人能回答这个问题的MySQL和Php特定部分,请随意编辑这个答案。下面的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
的表包含字段纬度
和经度
:
一个快速,简单和准确的(对于较小的距离)近似可以通过一个简单的。至少在我的路由算法中,与正确的计算相比,我得到了20%的提升。在Java代码中,它看起来像:
public double approxDistKm(double fromLat, double fromLon, double toLat, double toLon) {
double dLat = Math.toRadians(toLat - fromLat);
double dLon = Math.toRadians(toLon - fromLon);
double tmp = Math.cos(Math.toRadians((fromLat + toLat) / 2)) * dLon;
double d = dLat * dLat + tmp * tmp;
return R * Math.sqrt(d);
}
不确定MySQL(对不起!)
确保您了解限制(assertEquals的第三个参数表示以公里为单位的精度):
下面是一个非常详细的描述,介绍了使用MySQL进行地理距离搜索的方法,这是一个基于Haversine公式到MySQL的实现的解决方案。完整的解决方案描述,包括理论、实现和进一步的性能优化。虽然在我的例子中,空间优化部分没有正常工作。
选择
((acos(sin(('$latitude'*pi()/180))*sin('lat`*pi()/180))+cos('$latitude'*pi()/180))
*cos(('lat`*pi()/180))*cos(('$longitude'-'lng`)*pi()/180))*180/pi())*60*1.1515)
作为距离
从表having distance中,关于如何安装为MySQL插件的完整代码如下:
我去年发表了这篇评论。既然@TylerCollier好心地建议我将其作为答案发布,就在这里
另一种方法是编写一个自定义UDF函数,返回两点之间的哈弗斯线距离。此功能可接收以下输入:
lat1 (real), lng1 (real), lat2 (real), lng2 (real), type (string - optinal - 'km', 'ft', 'mi')
我们可以这样写:
SELECT id, name FROM MY_PLACES WHERE haversine_distance(lat1, lng1, lat2, lng2) < 40;
这是距离计算q
float lat = 24.235f;
float lon = 47.234f;
CalcDistance dist = new CalcDistance();
double res = 15.051;
assertEquals(res, dist.calcDistKm(lat, lon, lat - 0.1, lon + 0.1), 1e-3);
assertEquals(res, dist.approxDistKm(lat, lon, lat - 0.1, lon + 0.1), 1e-3);
res = 150.748;
assertEquals(res, dist.calcDistKm(lat, lon, lat - 1, lon + 1), 1e-3);
assertEquals(res, dist.approxDistKm(lat, lon, lat - 1, lon + 1), 1e-2);
res = 1527.919;
assertEquals(res, dist.calcDistKm(lat, lon, lat - 10, lon + 10), 1e-3);
assertEquals(res, dist.approxDistKm(lat, lon, lat - 10, lon + 10), 10);
set @latitude=53.754842;
set @longitude=-2.708077;
set @radius=20;
set @lng_min = @longitude - @radius/abs(cos(radians(@latitude))*69);
set @lng_max = @longitude + @radius/abs(cos(radians(@latitude))*69);
set @lat_min = @latitude - (@radius/69);
set @lat_max = @latitude + (@radius/69);
SELECT * FROM postcode
WHERE (longitude BETWEEN @lng_min AND @lng_max)
AND (latitude BETWEEN @lat_min and @lat_max);
select
(((acos(sin(('$latitude'*pi()/180)) * sin((`lat`*pi()/180))+cos(('$latitude'*pi()/180))
* cos((`lat`*pi()/180)) * cos((('$longitude'- `lng`)*pi()/180))))*180/pi())*60*1.1515)
AS distance
from table having distance<22;
lat1 (real), lng1 (real), lat2 (real), lng2 (real), type (string - optinal - 'km', 'ft', 'mi')
SELECT id, name FROM MY_PLACES WHERE haversine_distance(lat1, lng1, lat2, lng2) < 40;
SELECT id, name FROM MY_PLACES WHERE haversine_distance(lat1, lng1, lat2, lng2, 'ft') < 25;
double
haversine_distance( UDF_INIT* initid, UDF_ARGS* args, char* is_null, char *error ) {
double result = *(double*) initid->ptr;
/*Earth Radius in Kilometers.*/
double R = 6372.797560856;
double DEG_TO_RAD = M_PI/180.0;
double RAD_TO_DEG = 180.0/M_PI;
double lat1 = *(double*) args->args[0];
double lon1 = *(double*) args->args[1];
double lat2 = *(double*) args->args[2];
double lon2 = *(double*) args->args[3];
double dlon = (lon2 - lon1) * DEG_TO_RAD;
double dlat = (lat2 - lat1) * DEG_TO_RAD;
double a = pow(sin(dlat * 0.5),2) +
cos(lat1*DEG_TO_RAD) * cos(lat2*DEG_TO_RAD) * pow(sin(dlon * 0.5),2);
double c = 2.0 * atan2(sqrt(a), sqrt(1-a));
result = ( R * c );
/*
* If we have a 5th distance type argument...
*/
if (args->arg_count == 5) {
str_to_lowercase(args->args[4]);
if (strcmp(args->args[4], "ft") == 0) result *= 3280.8399;
if (strcmp(args->args[4], "mi") == 0) result *= 0.621371192;
}
return result;
}
SELECT * FROM (SELECT *,(((acos(sin((43.6980168*pi()/180)) *
sin((latitude*pi()/180))+cos((43.6980168*pi()/180)) *
cos((latitude*pi()/180)) * cos(((7.266903899999988- longitude)*
pi()/180))))*180/pi())*60*1.1515 ) as distance
FROM wp_users WHERE 1 GROUP BY ID limit 0,10) as X
ORDER BY ID DESC
$objectQuery = "SELECT table_master.*, ((acos(sin((" . $latitude . "*pi()/180)) * sin((`latitude`*pi()/180))+cos((" . $latitude . "*pi()/180)) * cos((`latitude`*pi()/180)) * cos(((" . $longitude . "- `longtude`)* pi()/180))))*180/pi())*60*1.1515 as distance FROM `table_post_broadcasts` JOIN table_master ON table_post_broadcasts.master_id = table_master.id WHERE table_master.type_of_post ='type' HAVING distance <='" . $Radius . "' ORDER BY distance asc";
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
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`
Select st_distance_sphere(POINT(-2.997065, 53.404146 ), POINT(58.615349, 23.56676 ))/1000 as distcance
SELECT
*,
(6371 * ACOS(COS(RADIANS(56.946285)) * COS(RADIANS(Y(coordinates)))
* COS(RADIANS(X(coordinates)) - RADIANS(24.105078)) + SIN(RADIANS(56.946285))
* SIN(RADIANS(Y(coordinates))))) AS distance
FROM places
WHERE MBRContains
(
LineString
(
Point (
24.105078 + 15 / (111.320 * COS(RADIANS(56.946285))),
56.946285 + 15 / 111.133
),
Point (
24.105078 - 15 / (111.320 * COS(RADIANS(56.946285))),
56.946285 - 15 / 111.133
)
),
coordinates
)
HAVING distance < 15
ORDER By distance
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;