Sometimes we need to find out list of places that are within a certain radius from a center place where coordinates of the places are saved in the database. Now we have 2 solutions for this – either loop through all the places find the distance from the center point and keep the places that have distance less or equal to the radius, or make an sql function to find the distance of two places, and select the places having distance less or equal to the radius. Obviously the second option is better than the first one. So I have written a Mysql function that does the work for you.
In my case, the coordinates were saved in the database as a string of the form "10.1357002, 49.9225563, 0". This is the standard format of coordinates that is used by many (for example Google map). The first element is the longitude, second one is latitude and we can ignore the third (always 0). So here is the Mysql function that returns the distance between 2 coordinates in Miles.
DELIMITER $$
DROP FUNCTION IF EXISTS `GetDistance`$$
CREATE FUNCTION `GetDistance`(coordinate1 VARCHAR(120), coordinate2 VARCHAR(120))
RETURNS VARCHAR(120)
BEGIN
DECLARE pos_comma1, pos_comma2 INT;
DECLARE lon1, lon2, lat1, lat2, distance DECIMAL(12,8);
select locate(',', coordinate1) into pos_comma1;
select locate(',', coordinate1, pos_comma1+1) into pos_comma2;
select CAST(substring(coordinate1, 1, pos_comma1-1) as DECIMAL(12,8)) into lon1;
select CAST(substring(coordinate1, pos_comma1+1, pos_comma2-pos_comma1-1) as DECIMAL(12,8)) into lat1;
select locate(',', coordinate2) into pos_comma1;
select locate(',', coordinate2, pos_comma1+1) into pos_comma2;
select CAST(substring(coordinate2, 1, pos_comma1-1) as DECIMAL(12,8)) into lon2;
select CAST(substring(coordinate2, pos_comma1+1, pos_comma2-pos_comma1-1) as DECIMAL(12,8)) into lat2;
select ((ACOS(SIN(lat1 * PI() / 180) * SIN(lat2 * PI() / 180) + COS(lat1 * PI() / 180) * COS(lat2 * PI() / 180) * COS((lon1 - lon2) * PI() / 180)) * 180 / PI()) * 60 * 1.1515) into distance;
RETURN distance;
END$$
DELIMITER ;
Example Usage:
Lets say you have to find out all the postcodes that are in 40 miles radius from a place having coordinate "10.1357002, 49.9225563, 0". You have a table POSTCODES having fields id, postcode, coordinates. So your sql should look like this:
select id, postcode from POSTCODES where GetDistance("10.1357002, 49.9225563, 0", coordinates) <= 40;
Isn’t it simple?


#1 by Ryan McDermott on July 27th, 2009
how well would this work with 1,000,000+ locations to sort through?
#2 by Imran on July 27th, 2009
Good question!
I just tried a simple php script today that executes the select statement with 26 records in the table. It took 0.004187 ms. So simple calculation says, it should take around (0.004187/26) * 1000000 = 161.04ms = .16 sec to execute with 1,000,000 record. Is that good enough?
Let me know if this can be optimized somehow.
#3 by jb on October 23rd, 2009
Could this function be written to calculate driving distance?
#4 by Imran on October 23rd, 2009
This is the simplest form of finding perpendicular distance between 2 locations. To find out the driving distance, you need to know the lat/long of all the nodes of the roads and sum up the distances of all the nodes.
In that case you may use this function with some loops over the nodes.
#5 by jb on October 24th, 2009
I think I will just to the function as it is. I have way to many records as it is. I appreciate you posting this function here, it will be very helpful.
#6 by jb on October 24th, 2009
To use this function with lat and long in separate columns would I just do something like :
CREATE FUNCTION `GetDistance`(lat VARCHAR(120),long VARCHAR(120), coordinate2 VARCHAR(120))
RETURNS VARCHAR(120)
BEGIN
DECLARE pos_comma1, pos_comma2 INT;
DECLARE lon1, lon2, lat1, lat2, distance DECIMAL(12,8);
select locate(lat) into pos_comma1;
select locate(long) into pos_comma2;
select CAST(long) as DECIMAL(12,8)) into lon1;
select CAST(lat) as DECIMAL(12,8)) into lat1;
…..
?
#7 by Imran on October 24th, 2009
Yes, it should work like that. Let me know how it performs with large number of data. The database I used it for still does not have a good number of data.
Good to know that it helped you
#8 by jb on October 24th, 2009
I will do that. I just realized that I don’t have the latitude and longitude values for the majority of my database. I probably wont have a good number to go through for quite some time.
#9 by js on November 24th, 2009
Something funny I noticed about this code. When you use ORDER By getDistance(.. it orders the results with a split between east and west. All results to the east are listed before the west. It\’s rather strange.
Example: 8, 22, 28, 2, 123.
I’m not sure how to go about fixing that.
#10 by imran on November 24th, 2009
Sounds interesting. I’ll have a look later. A bit busy at the moment. Thanks for sharing.
#11 by Tom on May 11th, 2010
great… thanks. Also, the ad on the left hand side of the page is a bit obtrusive. It blocks part of the content of your article. I had to use firebug to remove it so I could see the full article. Please consider an alternative placement
#12 by Imran on May 11th, 2010
Sorry that the ad was bugging, which FF version you used, I fixed the position so that it does not come over the content.