MySQL在存經緯度的資料庫查詢最近距離的應用

2021-10-05 19:50:02 字數 3170 閱讀 6997

距離可以用r*arccos[cos(y1)*cos(y2)*cos(x1-x2)+sin(y1)*sin(y2)]來算

r是地球半徑6370km,x是經度,y是緯度

指定乙個經緯度,給定乙個範圍值(單位:千公尺),查出在經緯度周圍這個範圍內的資料。 

經度:113.914619 

緯度:22.50128 

範圍:2km 

longitude為資料表經度字段 

latitude為資料表緯度字段 

sql在mysql下測試通過,其他資料庫可能需要修改 

sql語句如下: 

select * from location where sqrt( ( ((113.914619-longitude)*pi()*12656*cos(((22.50128+latitude)/2)*pi()/180)/180) * ((113.914619-longitude)*pi()*12656*cos (((22.50128+latitude)/2)*pi()/180)/180) ) + ( ((22.50128-latitude)*pi()*12656/180) * ((22.50128-latitude)*pi()*12656/180) ) )<2

最近遇到了乙個問題,通過不斷的嘗試最終將某句原本佔據近1秒的查詢優化到了0.01秒,效率提高了100倍.

問題是這樣的,有一張存放使用者居住地點經緯度資訊的mysql資料表,表結構可以簡化 為:id(int),longitude(long),latitude()long. 而業務系統中有乙個功能是查詢離某個使用者最近的其餘數個使用者,通過**分析,可以確定原先的做法基本是這樣的:

//需要查詢的使用者的座標

$lat=20; $lon=20;//執行查詢,算出該使用者與所有其他使用者的距離,取出最近的10個 $sql='select * from users_location order by acos(sin(('.$lat.' * 3.1415) / 180 ) *sin((latitude * 3.1415) / 180 ) +cos(('.$lat.' * 3.1415) / 180 ) * cos((latitude * 3.1415) / 180 ) *cos(('.$lon.' * 3.1415) / 180 - (longitude * 3.1415) / 180 ) ) * 6380 asc limit 10';
而這條sql執行的速度卻非常緩慢,用了近1秒的時間才返回結果,應該是因為order裡的子語句用了太多的數學計算公式,導致整體的運算速度下降.

而在實際的使用中,不太可能會發生需要計算該使用者與所有其他使用者的距離,然後再排序的情況,當使用者數量達到乙個級別時,就可以在乙個較小的範圍裡進行搜尋,而非在所有使用者中進行搜尋.

所以對於這個例子,我增加了4個where條件,只對於經度和緯度大於或小於該使用者1度(111公里)範圍內的使用者進行距離計算,同時對資料表中的經度和緯度兩個列增加了索引來優化where語句執行時的速度.

最終的sql語句如下

$sql='select * from users_location where latitude > '.$lat.'-1 and latitude < '.$lat.'+1 and longitude > '.$lon.'-1 and longitude < '.$lon.'+1 order by acos(sin(('.$lat.' * 3.1415) / 180 ) *sin((latitude * 3.1415) / 180 ) +cos(('.$lat.' * 3.1415) / 180 ) * cos((latitude * 3.1415) / 180 ) *cos(('.$lon.'* 3.1415) / 180 - (longitude * 3.1415) / 180 ) ) * 6380 asc limit 10';

經過優化的sql大大提高了執行速度,在某些情況下甚至有100倍的提公升.這種從業務角度出發,縮小sql查詢範圍的方法也可以適用在其他地方.

全域性儲存方法的sql

set global log_bin_trust_function_creators=1;

delimiter

createdefiner=currentuserfunction『fngetdistanceinkilometers『(『lon1『float,『lat1『float,『lon2『float,『lat2『float,『range『int)returnsdoublebegindeclareddouble;declareradiusint;setradius=6378140;setd=(2∗atan2(sqrt(sin((lat1−lat2)∗pi()/180/2)∗sin((lat1−lat2)∗pi()/180/2)+cos(lat2∗pi()/180)∗cos(lat1∗pi()/180)∗sin((lon1−lon2)∗pi()/180/2)∗sin((lon1−lon2)∗pi()/180/2)),sqrt(1−sin((lat1−lat2)∗pi()/180/2)∗sin((lat1−lat2)∗pi()/180/2)+cos(lat2∗pi()/180)∗cos(lat1∗pi()/180)∗sin((lon1−lon2)∗pi()/180/2)∗sin((lon1−lon2)∗pi()/180/2))))∗radius;returntruncate(d/1000,1);endcreatedefiner=currentuserfunction『fngetdistanceinkilometers『(『lon1『float,『lat1『float,『lon2『float,『lat2『float,『range『int)returnsdoublebegindeclareddouble;declareradiusint;setradius=6378140;setd=(2∗atan2(sqrt(sin((lat1−lat2)∗pi()/180/2)∗sin((lat1−lat2)∗pi()/180/2)+cos(lat2∗pi()/180)∗cos(lat1∗pi()/180)∗sin((lon1−lon2)∗pi()/180/2)∗sin((lon1−lon2)∗pi()/180/2)),sqrt(1−sin((lat1−lat2)∗pi()/180/2)∗sin((lat1−lat2)∗pi()/180/2)+cos(lat2∗pi()/180)∗cos(lat1∗pi()/180)∗sin((lon1−lon2)∗pi()/180/2)∗sin((lon1−lon2)∗pi()/180/2))))∗radius;returntruncate(d/1000,1);end

delimiter $$;

MySQL在存經緯度的資料庫查詢最近距離的應用

距離可以用r arccos cos y1 cos y2 cos x1 x2 sin y1 sin y2 來算 r是地球半徑6370km,x是經度,y是緯度 指定乙個經緯度,給定乙個範圍值 單位 千公尺 查出在經緯度周圍這個範圍內的資料。經度 113.914619 緯度 22.50128 範圍 2km...

MySQL在存經緯度的資料庫查詢最近距離的應用

距離可以用r arccos cos y1 cos y2 cos x1 x2 sin y1 sin y2 來算 r是地球半徑6370km,x是經度,y是緯度指定乙個經緯度,給定乙個範圍值 單位 千公尺 查出在經緯度周圍這個範圍內的資料。經度 113.914619 緯度 22.50128 範圍 2km ...

sql 資料庫計算經緯度

1.也是從網路上找的方法辣 實質就是在 資料庫 建立乙個函式,供呼叫啦 create function dbo fngetdistance latbegin 開始經度 lngbegin 開始維度 latbegin real,lngbegin real,latend real,lngend real ...