mysql按经纬度排序_mysql根据经纬度查排序
⽬的是,根据经纬度,实现查其附近的⼈或地点,LBS场景。
做基于地理位置的应⽤后台,没有使⽤mongodb,看了⽹上的很多答案,其实也就是⼏份答案⽽已,天下⽂章⼀⼤抄。
这⾥综合⽹上的,结合⾃⾝,总结出了⼏种根据经纬度的排序算法,测试可⽤,性能也还可以。逐步递进优化,不同阶段,不同使⽤。
这⾥x为纬度,y为经度。
PS:SQL代码不完整,仅供阅读参考,理解意思就好。
⼀、MySql不使⽤空间函数,简单版
1.粗算,根据场景得到⼀个range,计算经纬度,得到的是⼀个矩形区域(A),不精确,但是已经有范围这个雏形了,最容易实现的⽅式之⼀。
where latitude>y-range
and latitude
and longitude>x-range
and longitude
order by abs(longitude -x)+abs(latitude -y)
limit 10;
2.使⽤PHP函数计算出距离,排序即可。
排序的话,⾃⼰灵活实现。很多语⾔都有封装排序算法,效率也挺⾼的。
⼆、Mysql不使⽤空间函数,优化版
这⾥的优化是对(⼀)中range的优化。根据范围半径,计算出经纬度的变化范围,得到⼀个⽐较准确的range,这⾥的范围(B)是圆形的(因为$radius是俩点间的距离)。
但是筛选时候的范围(C)是矩形,所以精确上来说, 圆B是矩形C的内切圆,不在圆B但是在矩形C中的点也会出现在我们的SQL结果中。但是已经⽐(⼀)要好很多了。
$radius = 1;//半径范围,单位km
$rangeLat = 180 / pi() * $radius / 6372.797;//纬度范围
$rangeLng = $rangeLat / cos($x * pi() / 180.0); //经度范围
$maxLat = $x + $range; //x1
$minLat = $x - $range; //x0
$maxLng = $y + $lngR; //y1
$minLng = $y - $lngR; //y0
我见过把这个计算带⼊到SQL中的,⼀⼤串SQL,这种计算本来就不是SQL该有的,不推荐这样做。
三、MySql使⽤空间函数
在(⼆)中,我们得到了4个点。这个就是矩形范围,我们只要判断是否在这个矩形内就好了。其实⽤到mysql的空间函数可以⽀持任意多边形。
还⽀持索引优化,MyISAM 才能建⽴空间索引,MySql 5.7版本 InnoDB 也⽀持空间索引了。
优化程序将调查可⽤的空间索引是否能包含在使⽤某些函数的查询搜索中,如WHERE⼦句中的MBRContains()或MBRWithin()函数。
--19.6.2. 使⽤空间索引
这⾥的核⼼思想就是⽤⼀个范围判断某个点是否在这个范围内。
在数据库有⼀个类型为geometry的列g。
select id,AsText(g) from geom
where MBRContains(GeomFromText('Polygon((x0 y0,x1 y0,x1 y1,x0 y1,x0 x0))'),g);
即可准确筛选出在这个范围内的点。即使后⾯跟ORDER BY限制距离性能也没有太⼤影响。
SELECT id, AsText(g), SQRT(POW( ABS( X(g) - X(x)), 2) + POW( ABS(Y(g) - Y(y)), 2 )) AS distance
FROM geom[*1]
WHERE MBRIntersects(g, GeomFromText('Polygon((x0 y0,x1 y0,x1 y1,x0 y1,x0 x0))'))[*2]
AND SQRT(POW( ABS( X(g) - X(x)), 2) + POW( ABS(Y(g) - Y(y)), 2 )) < radius[*3]
ORDER BY distance;[*4]
1:从geom表中根据俩点间的距离公式计算结果,命名为distance
2:条件1,g列中的点和算出来的范围相交!相交!相交!注意我⽤的是MBRIntersects(),不是MBRContains()
3:条件2,distance⼩于给定的半径radius
4:根据distance排序
上⾯是根据官⽅⽂档写的⽰例代码,⽐较重要,如果你没看懂,没关系,我来举个栗⼦
这⾥选⽤MBRContains()来举例⼦,你可以⾃⼰实验下MBRWithin()函数,注意参数顺序就好了,我这⾥得到的结果是⼀样的。函数⽤法:MBRContains(g1,g2)
函数说明:返回1或0以指明g1的最⼩边界矩形是否包含g2的最⼩边界矩形
函数已经说明了是g1是否包含g2,所以不要弄反了;这⾥的矩形⽀持任意多变形
⽬标点:D(1,1)
mongodb和mysql结合也可以是范围哟,见注释*1
范围:E(0 0,0 3,3 3,3 0,0 0),闭合矩形,其实⽀持任意闭合多边形
SELECT id,name
from geom
where MBRContains(GeomFromText('Polygon((0 0,0 3,3 3,3 0,0 0))'),
GeomFromText('Point(1 1)'));[*1]
1:这⾥的⽬标点D也⽀持任意多边形,参数不再是Point()⽽是Polygon()
这条SQL可以解释为判断点D是否在范围内E。
SELECT id,name,AsText(g)[*1]
from geom
where MBRContains(GeomFromText('Polygon((0 0,0 3,3 3,3 0,0 0))'),
g);
1:因为g列是geometry类型的,所以要⽤AsText转换下再展现出来
这条SQL可以解释为列出数据库中所有包含在范围E中的点。
更多相交、包含、接触等⽅法见上⾯开发⽂档 《19.5.5. 关于⼏何最⼩边界矩形(MBR)的关系》
四、geohash
这个GeoHash是将⼆维的经纬度转换成字符串,字符串长度越长,精度就越精细。俩个字符串长度匹配的位数越多,就越接近,绝⼤部分情况看起来是这个样⼦的,但有例外。
类似于:(G->)|J......我|K......|(
因为GeoHash是将区域划分为⼀个个规则矩形,所以在同⼀个矩形中,GeoHash是⼀样的,但是会出现⼀个边际问题:G、H俩个左右相邻的矩形,我在G的右边际处(右边际和H相邻),餐厅J在G的左边际,餐厅K也在H的左边际,通过GeoHash得出来的结果是餐厅J离我更近,显然不合理。
可以通过加⼤矩形区域的精细程度和扩⼤相似范围解决。
根据匹配相应的位数,在mysql加⼊索引,可以极⼤提⾼效率。GeoHash和经纬度的转换,⽹上都有现成的代码,这⾥不再展⽰,PHP还有对应的C拓展能提⾼计算速度。
*在纬度相等的情况下:
*经度每隔0.00001度,距离相差约1⽶;
*每隔0.0001度,距离相差约10⽶;
*每隔0.001度,距离相差约100⽶;
*每隔0.01度,距离相差约1000⽶;
*每隔0.1度,距离相差约10000⽶。
*在经度相等的情况下:
*纬度每隔0.00001度,距离相差约1.1⽶;
*每隔0.0001度,距离相差约11⽶;
*每隔0.001度,距离相差约111⽶;
*每隔0.01度,距离相差约1113⽶;
*每隔0.1度,距离相差约11132⽶。
Geohash,如果geohash的位数是6位数的时候,⼤概为附近1千⽶。
五、Redis GeoHash
Redis也能玩定位?
sure!并且效率奇⾼!
虽然也是通过GeoHash(⾼性能、⾼精度版)来实现的,但是它封装了很多有⽤的⽅法,直接使⽤经纬度即可操作,能直接根据距离返回对应的点,⽀持直接返回json,还⽀持排序输出。
毕竟是Redis,持久化和容量都是要考虑的问题。
但是Redis从来不是孤军奋战的⼯具。
可以和MySql搭配,放在数据库前扛着,⾥⾯存储⾼频定位点,MySql也⽀持定位(⽅案三),合理使⽤应该很好的MySql定位解决⽅案了。
以上就是根据现有资料整理的MySql经纬度经纬解决⽅案,如果有更好的⽅案,欢迎评论区讨论。
happy coding~

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。