Null returns for distance - mysql

Total SQL newb here. I've created two SQL tables, one containing information about hotels and the other containing information about attractions. I'm trying to create a query where I can input a postcode of an attraction, and have the database return the distance of hotels within a 1, 5 and 10 mile radius.
SELECT
hotels.*,
attractions.*,
( ( ACOS( SIN(hotels.Hotel_Lat * PI() / 180)
* SIN(attractions.Attraction_Lat * PI() / 180)
+
COS(hotels.Hotel_Long * PI() / 180)
* COS(attractions.Attraction_Long * PI() / 180)
* COS((hotels.Hotel_Long - attractions.Attraction_Long) * PI() / 180)
) * 180 / PI()
) * 60 * 1.1515
) as distance
FROM hotels
JOIN attractions
This query returns 'Null' for distances. My tables are populated with lat longs as well as postcodes. Any ideas why please? I have negative value longitudes if that makes a difference?
Using MySQL Community server 5.6.17.
Sample Data - There are 7 rows of data in hotels.
Hotel_Name | Hotel_Address | Hotel_Lat | Hotel_Long
a ST158DH 52.906438 -2.145523
b ST161LF 52.827959 -2.129709
Attraction_Name | Attraction_Postcode | Attraction_Lat | Attraction_Long
a ST180BA 52.839509 -2.056964
b ST180TG 52.832820 -2.091124
There are four attractions in total.

Using the Haversine formula, which is well behaved even at very small distances, try this code (or equivalent for MySQL):
select
Hotel_Name
,Hotel_Address
,Attraction_Name
,Attraction_Address
,round(2 * atn2(sqrt(a),sqrt(1-a)), 4) as DeltaRad
,round(2 * atn2(sqrt(a),sqrt(1-a)) * (180.0 / pi()), 2) as DeltaDeg
,round(2 * atn2(sqrt(a),sqrt(1-a)) * (180.0 / pi()) * 60, 2) as DeltaNMs
,round(2 * atn2(sqrt(a),sqrt(1-a)) * 6371.0, 2) as DeltaKMs
from (
select
Hotel_Name
,Hotel_Address
,Attraction_Name
,Attraction_Address
, SinHalfDeltaLatRad * SinHalfDeltaLatRad
+ CosCosLat
* SinHalfDeltaLngRad * SinHalfDeltaLngRad as a
from (
SELECT
Hotel_Name
,Hotel_Address
,Attraction_Name
,Attraction_Address
,sin((h.Lat - a.Lat) * PI() / 180.0 / 2.0) as SinHalfDeltaLatRad
,sin((h.Lng - a.Lng) * PI() / 180.0 / 2.0) as SinHalfDeltaLngRad
,cos(a.Lat) * cos(a.Lat) as CosCosLat
FROM hotels as h
cross JOIN attractions as a
) t
) t
Yielding for your sample data:
Hotel_Name Hotel_Address Attraction_Name Attraction_Address DeltaRad DeltaDeg DeltaNMs DeltaKMs
-------------------- ------------- -------------------- ------------------ ---------------------- ---------------------- ---------------------- ----------------------
HotelA ST158DH AttractionA ST180BA 0.0018 0.1 6.02 11.15
HotelB ST161LF AttractionA ST180BA 0.0011 0.06 3.75 6.94
HotelA ST158DH AttractionB ST180TG 0.0015 0.09 5.2 9.63
HotelB ST161LF AttractionB ST180TG 0.0006 0.03 1.97 3.64

Related

MySQL query to get route between 2 lat-lng coordinates

I'm looking for a query to get the route between two points.
I have this link, let's say:
example.com/any? lat1 = 31.0000 & lng1 = 31.0000 & lat2 = 31.0000 & lng2 = 31.0000
So inside the function, I want to write a query that goes to the table example,
id | lat1 | lng1 | lat2 | lng2 | time | distance_between_inKm
1 | 31.0000 | 31.0000 | 31.0000 | 31.0000 | 30 | 20
So now I want to go to the link and get the nearest || Closest* route or row
I'm getting these from link lat1 & lng1 and lat2 & lng2 so I want to get the closest for the two.
So what I want is that I have a table contains static data for polylines and info for 2 points so I want to use them like Google Matrix api but from my server, let's say:
Update
I tried this query but it ignore the AND:
SELECT id,distance_between,time_between,start_latitude, start_longitude,end_latitude,end_longitude, SQRT(
POW(69.1 * (start_latitude - 31.908482676577364), 2) +
POW(69.1 * (35.1666776731924 - start_longitude) * COS(start_latitude / 57.3), 2))
AND
SQRT(
POW(69.1 * (end_latitude - 31.966051), 2) +
POW(69.1 * (35.894587 - end_longitude) * COS(end_latitude / 57.3), 2))
AS distance
FROM matrix_api USE INDEX (start_latitude,start_longitude,end_latitude,end_longitude,distance_between,time_between) HAVING distance < 0.2 ORDER BY distance LIMIT 1;
So I want something like that give input 1 and 2 and give me the row that has the 2
Close for the start latlngs and the end latlngs at the same time
Update
I tried this too, no luck still:
SELECT id,distance_between,time_between,start_latitude, start_longitude,end_latitude,end_longitude,
SQRT(
POW(69.1 * (start_latitude - 32.016659), 2) +
POW(69.1 * (35.727839 - start_longitude) * COS(start_latitude / 57.3), 2) AND
POW(69.1 * (end_latitude - 31.966051), 2) +
POW(69.1 * (35.894587 - end_longitude) * COS(end_latitude / 57.3), 2)
)
AS distance
FROM matrix_api USE INDEX (start_latitude,start_longitude,end_latitude,end_longitude,distance_between,time_between) HAVING distance < 0.2 ORDER BY distance LIMIT 1;
Sample input:
lat1=0.0000 lng1=0.0000
lat2=0.0000 lng2=0.0000
Output:
The row that I already have in table Distance between: 0.0 And time between: 0.0
So inside table we have the lat1 and lng1 lat2 lng2 And distance between. I want to get the row with nearest lat1 lng1 in same time nearest to lat2 lng2. But here the 2 are in same row saved so I want it like Google API Matrix closest distance between inside the row I need or found.
A small change in your query:
SELECT id,distance_between,time_between
,start_latitude, start_longitude,end_latitude,end_longitude
FROM
(
SELECT id,distance_between,time_between
,start_latitude, start_longitude,end_latitude,end_longitude,
SQRT(
POW(69.1 * (start_latitude - 31.908482676577364), 2) +
POW(69.1 * (35.1666776731924 - start_longitude) * COS(start_latitude / 57.3), 2)
) as startlatlngDistance
,SQRT(
POW(69.1 * (end_latitude - 31.966051), 2) +
POW(69.1 * (35.894587 - end_longitude) * COS(end_latitude / 57.3), 2))
AS endlatlngDistance
FROM matrix_api
USE INDEX (start_latitude,start_longitude,end_latitude
,end_longitude,distance_between,time_between)
) as A
WHERE startlatlngDistance < 0.2 AND endlatlngDistance < 0.2
ORDER BY startlatlngDistance LIMIT 1;

PostgreSQL - column doesn't exist (WHERE with AS)

i am a bit newbie to PostgreSQL, but i have a few experiences with MySQL.
The Postgres is showing me and error - COLUMN doesn't exist, but this is a "virtual column", created by AS.
Code, which is working very well in MySQL:
SELECT place.*, 3956 * 2 * ASIN(SQRT( POWER(SIN((place.lattitude - $1) * pi() / 180 / 2), 2) + COS($2 * pi() / 180) * COS(place.lattitude * pi() / 180) *POWER(SIN(($3 - place.longitude) * pi() / 180 / 2), 2) )) AS "distance" FROM place WHERE place.longitude BETWEEN $4 AND $5 AND place.lattitude BETWEEN $6 AND $7 HAVING "distance" < $8 ORDER BY "distance" LIMIT 10
But Postgres is showing Column "distance" does not exist.
How can I rewrite it? (Please write full SQL query, not "How to")
$number is variable (against SQL injection)
Thanks
Use a subquery:
SELECT p.*
FROM (SELECT place.*, 3956 * 2 * ASIN(SQRT( POWER(SIN((place.lattitude - $1) * pi() / 180 / 2), 2) + COS($2 * pi() / 180) * COS(place.lattitude * pi() / 180) *POWER(SIN(($3 - place.longitude) * pi() / 180 / 2), 2) )) AS "distance"
FROM place
WHERE place.longitude BETWEEN $4 AND $5 AND
place.lattitude BETWEEN $6 AND $7
) p
WHERE "distance" < $8
ORDER BY "distance"
LIMIT 10;
You don't want to use a subquery in MySQL because it materializes the intermediate result. Other databases are smarter in how they optimize queries, and do not necessarily materialize subqueries.

Find all nearest zip codes with lat and long from a particular latitude and longitude in sql query

I've a database table in which there are 4 columns.
1. id
2. person_name
3. country
4. zip_code
Now I want all the zip codes with their real latitude and longitude which come in a given radius of 10 mile from a given lat long.
suppose my latitude and longitudes are (19.24947300,72.85681400) and distance is 10 mile, then what SQL query should I make to return all nearest zip codes and their lat longs.
I only have the following query
SELECT ((ACOS(SIN($lat * PI() / 180) * SIN(lat * PI() / 180) + COS($lat * PI()
/ 180) * COS(lat * PI() / 180) * COS(($lon – lon) * PI() / 180)) * 180 / PI()) * 60 *
1.1515) AS `distance` FROM `members` HAVING `distance`<=’10’ ORDER BY `distance` ASC
But it requires the lat longs of all zip codes in the table but I want them in run time.
I'm not sure I understand what you mean by
But it requires the lat longs of all zip codes in the table but I want them in run time.
Since you want a SQL query, I assume that you have a table with every zip code's latitude and longitude.
You probably shouldn't do all that math in your SQL query. I would first pull every zip code that is in a 10 mile x 10 mile square centered in your queried position and then, if you really need just those that are positively in a 10 mile radius, check in your program (I assume you are using PHP, since you have variables starting with $).
SELECT zip, latitude, longitude FROM zip_positions
WHERE latitude > 19.24947300 - DISTANCE and latitude < 19.24947300 + DISTANCE
AND longitude > 72.85681400 - DISTANCE and longitude < 72.85681400 + DISTANCE
where DISTANCE is a rough equivalent to 10 miles in latitude/longitude that you would have to calculate.
Then you would, if you need it, check in PHP (or whatever language you're using) every result to see if it is indeed less than 10 miles from the point queried. I can't help you as to how to precisely convert latitude and longitude to distance in miles.
I assume this abandoned question was resolved, but let me share my opinions here in case someone else is wrestling with this one. I present 3 options for letting closet zip from DB using lat long as the lookup:
DB LOOKUP OPTION 1 (QUICK AND DIRTY):
SET #lat = 41.675868;
SET #long = -73.864484;
SET #tol = .05;
SELECT * FROM mytable.ZipCodes
WHERE latitude >= #lat AND latitude <= #lat + #tol
AND longitude >= #long AND longitude <= #long + #tol
ORDER BY (ABS(latitude-longitude) - ABS(#lat-#long)) ASC
LIMIT 1
OPTION #2 (VARIATION ON #1)
$qry = "SELECT zipcode,cityname,stateabbr FROM dealer-site.ZipCodes WHERE (latitude >= {$lat} AND latitude <= {$lat}+1) AND (ABS(longitude) >= ABS({$long}) AND ABS(longitude) < ABS({$long})+1) ORDER BY latitude ASC LIMIT 1”;
OPTION #3 BEST METHOD CALCULATE . I WOULD TRY TO USE THIS:
SET #latitude = 41.675868;
SET #longitude = -73.864484;
SELECT
*
FROM
(SELECT
*,
(((ACOS(SIN((#latitude * PI() / 180)) *
SIN((latitude * PI() / 180)) + COS((#latitude * PI() / 180)) * COS((latitude * PI() / 180)) * COS(((#longitude - longitude) * PI() / 180)))) * 180 / PI()) * 60 * 1.1515 * 1.609344) AS distance
FROM
mytable.ZipCodes) ZipCodes
WHERE
distance <= 5
LIMIT 10
PHP MYSQL (LEGACY CALL) :
$qry = "SELECT zipcode
FROM (SELECT *,
(((ACOS(SIN(({$lat} * PI() / 180)) *
SIN((`latitude` * PI() / 180)) + COS(({$lat} * PI() / 180)) * COS((`latitude` * PI() / 180)) *
COS((({$long} - `longitude`) * PI() / 180)))) * 180 / PI()) * 60 * 1.1515 * 1.609344)
AS distance
FROM
`MyDB`.ZipCodes) ZipCodes
WHERE distance <= 5
LIMIT 1
";

Mysql distance function

I have the problems with my function which needs to calculate distance between given coordinates. As I can see problem is with negative values, and I am running out of ideas how to solve this, so if someone can help me I will really appreciate it!
DELIMITER $$
CREATE DEFINER=`sfff_user`#`%` FUNCTION `GetUserDistance`(lat VARCHAR (20), lon VARCHAR (20), userLat VARCHAR (20), userLon VARCHAR (20)) RETURNS int(11)
BEGIN
DECLARE distance INT (11);
IF ISNULL(lat) OR ISNULL(lon) OR lat = '' OR lon = '' THEN
RETURN 0;
ELSE
SELECT
3956 * 2 * ASIN(SQRT(POWER(SIN((lat - ABS(userLat)) * PI() / 180 / 2), 2) + COS(lat * PI() / 180) * COS(ABS(userLat) * PI() / 180) * POWER(SIN((lon - userLon) * PI() / 180 / 2), 2)))
INTO
distance;
RETURN distance;
END IF;
END
For example result for this call:
select GetUserDistance(44, 21, 44, 21) as distance; is 0 which is ok
But look at this:
select GetUserDistance('-15.4167', '28.2833', '-15.4167', '28.2833') as distance;
is 2129 which is insane!
So if you can take a look it would be verry nice to have correct function, since I am dying to solve this :(
Thanks.
According to this link, the formula should be without ABS:
3956 * 2 * ASIN(SQRT(POWER(SIN((lat - userLat) * PI() / 180 / 2), 2) + COS(lat * PI() / 180) * COS(ABS(userLat) * PI() / 180) * POWER(SIN((lon - userLon) * PI() / 180 / 2), 2)))
For safe side you should CAST VARCHAR to DECIMAL and as #Scharron said no need of ABS try:
CREATE FUNCTION `GetUserDistance`(arg_lat VARCHAR (20), arg_lon VARCHAR (20), arg_userLat VARCHAR (20), arg_userLon VARCHAR (20)) RETURNS int(11) NO SQL
BEGIN
DECLARE distance INT (11);
DECLARE lat, lon, userLat, userLon DECIMAL(14,4);
SET lat = CAST(arg_lat AS DECIMAL(14,4));
SET lon = CAST(arg_lon AS DECIMAL(14,4));
SET userLat = CAST(arg_userLat AS DECIMAL(14,4));
SET userLon = CAST(arg_userLon AS DECIMAL(14,4));
IF ISNULL(lat) OR ISNULL(lon) OR lat = '' OR lon = '' THEN
RETURN 0;
ELSE
SELECT
3956 * 2 * ASIN(SQRT(POWER(SIN((lat - userLat) * PI() / 180 / 2), 2) + COS(lat * PI() / 180) * COS(ABS(userLat) * PI() / 180) * POWER(SIN((lon - userLon) * PI() / 180 / 2), 2)))
INTO
distance;
RETURN distance;
END IF;
END

Find nearest latitude/longitude with an SQL query

I have latitude and longitude and I want to pull the record from the database, which has nearest latitude and longitude by the distance, if that distance gets longer than specified one, then don't retrieve it.
Table structure:
id
latitude
longitude
place name
city
country
state
zip
sealevel
SELECT latitude, longitude, SQRT(
POW(69.1 * (latitude - [startlat]), 2) +
POW(69.1 * ([startlng] - longitude) * COS(latitude / 57.3), 2)) AS distance
FROM TableName HAVING distance < 25 ORDER BY distance;
where [starlat] and [startlng] is the position where to start measuring the distance.
Google's solution:
Creating the Table
When you create the MySQL table, you want to pay particular attention to the lat and lng attributes. With the current zoom capabilities of Google Maps, you should only need 6 digits of precision after the decimal. To keep the storage space required for your table at a minimum, you can specify that the lat and lng attributes are floats of size (10,6). That will let the fields store 6 digits after the decimal, plus up to 4 digits before the decimal, e.g. -123.456789 degrees. Your table should also have an id attribute to serve as the primary key.
CREATE TABLE `markers` (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`name` VARCHAR( 60 ) NOT NULL ,
`address` VARCHAR( 80 ) NOT NULL ,
`lat` FLOAT( 10, 6 ) NOT NULL ,
`lng` FLOAT( 10, 6 ) NOT NULL
) ENGINE = MYISAM ;
Populating the Table
After creating the table, it's time to populate it with data. The sample data provided below is for about 180 pizzarias scattered across the United States. In phpMyAdmin, you can use the IMPORT tab to import various file formats, including CSV (comma-separated values). Microsoft Excel and Google Spreadsheets both export to CSV format, so you can easily transfer data from spreadsheets to MySQL tables through exporting/importing CSV files.
INSERT INTO `markers` (`name`, `address`, `lat`, `lng`) VALUES ('Frankie Johnnie & Luigo Too','939 W El Camino Real, Mountain View, CA','37.386339','-122.085823');
INSERT INTO `markers` (`name`, `address`, `lat`, `lng`) VALUES ('Amici\'s East Coast Pizzeria','790 Castro St, Mountain View, CA','37.38714','-122.083235');
INSERT INTO `markers` (`name`, `address`, `lat`, `lng`) VALUES ('Kapp\'s Pizza Bar & Grill','191 Castro St, Mountain View, CA','37.393885','-122.078916');
INSERT INTO `markers` (`name`, `address`, `lat`, `lng`) VALUES ('Round Table Pizza: Mountain View','570 N Shoreline Blvd, Mountain View, CA','37.402653','-122.079354');
INSERT INTO `markers` (`name`, `address`, `lat`, `lng`) VALUES ('Tony & Alba\'s Pizza & Pasta','619 Escuela Ave, Mountain View, CA','37.394011','-122.095528');
INSERT INTO `markers` (`name`, `address`, `lat`, `lng`) VALUES ('Oregano\'s Wood-Fired Pizza','4546 El Camino Real, Los Altos, CA','37.401724','-122.114646');
Finding Locations with MySQL
To find locations in your markers table that are within a certain radius distance of a given latitude/longitude, you can use a SELECT statement based on the Haversine formula. The Haversine formula is used generally for computing great-circle distances between two pairs of coordinates on a sphere. An in-depth mathemetical explanation is given by Wikipedia and a good discussion of the formula as it relates to programming is on Movable Type's site.
Here's the SQL statement that will find the closest 20 locations that are within a radius of 25 miles to the 37, -122 coordinate. It calculates the distance based on the latitude/longitude of that row and the target latitude/longitude, and then asks for only rows where the distance value is less than 25, orders the whole query by distance, and limits it to 20 results. To search by kilometers instead of miles, replace 3959 with 6371.
SELECT
id,
(
3959 *
acos(cos(radians(37)) *
cos(radians(lat)) *
cos(radians(lng) -
radians(-122)) +
sin(radians(37)) *
sin(radians(lat )))
) AS distance
FROM markers
HAVING distance < 28
ORDER BY distance LIMIT 0, 20;
This one is to find latitudes and longitudes in a distance less than 28 miles.
Another one is to find them in a distance between 28 and 29 miles:
SELECT
id,
(
3959 *
acos(cos(radians(37)) *
cos(radians(lat)) *
cos(radians(lng) -
radians(-122)) +
sin(radians(37)) *
sin(radians(lat )))
) AS distance
FROM markers
HAVING distance < 29 and distance > 28
ORDER BY distance LIMIT 0, 20;
https://developers.google.com/maps/articles/phpsqlsearch_v3#creating-the-map
The original answers to the question are good, but newer versions of mysql (MySQL 5.7.6 on) support geo queries, so you can now use built in functionality rather than doing complex queries.
You can now do something like:
select *, ST_Distance_Sphere( point ('input_longitude', 'input_latitude'),
point(longitude, latitude)) * .000621371192
as `distance_in_miles`
from `TableName`
having `distance_in_miles` <= 'input_max_distance'
order by `distance_in_miles` asc
The results are returned in meters. So if you want in KM simply use .001 instead of .000621371192 (which is for miles).
MySql docs are here
Here is my full solution implemented in PHP.
This solution uses the Haversine formula as presented in http://www.scribd.com/doc/2569355/Geo-Distance-Search-with-MySQL.
It should be noted that the Haversine formula experiences weaknesses around the poles. This answer shows how to implement the vincenty Great Circle Distance formula to get around this, however I chose to just use Haversine because it's good enough for my purposes.
I'm storing latitude as DECIMAL(10,8) and longitude as DECIMAL(11,8). Hopefully this helps!
showClosest.php
<?PHP
/**
* Use the Haversine Formula to display the 100 closest matches to $origLat, $origLon
* Only search the MySQL table $tableName for matches within a 10 mile ($dist) radius.
*/
include("./assets/db/db.php"); // Include database connection function
$db = new database(); // Initiate a new MySQL connection
$tableName = "db.table";
$origLat = 42.1365;
$origLon = -71.7559;
$dist = 10; // This is the maximum distance (in miles) away from $origLat, $origLon in which to search
$query = "SELECT name, latitude, longitude, 3956 * 2 *
ASIN(SQRT( POWER(SIN(($origLat - latitude)*pi()/180/2),2)
+COS($origLat*pi()/180 )*COS(latitude*pi()/180)
*POWER(SIN(($origLon-longitude)*pi()/180/2),2)))
as distance FROM $tableName WHERE
longitude between ($origLon-$dist/cos(radians($origLat))*69)
and ($origLon+$dist/cos(radians($origLat))*69)
and latitude between ($origLat-($dist/69))
and ($origLat+($dist/69))
having distance < $dist ORDER BY distance limit 100";
$result = mysql_query($query) or die(mysql_error());
while($row = mysql_fetch_assoc($result)) {
echo $row['name']." > ".$row['distance']."<BR>";
}
mysql_close($db);
?>
./assets/db/db.php
<?PHP
/**
* Class to initiate a new MySQL connection based on $dbInfo settings found in dbSettings.php
*
* #example $db = new database(); // Initiate a new database connection
* #example mysql_close($db); // close the connection
*/
class database{
protected $databaseLink;
function __construct(){
include "dbSettings.php";
$this->database = $dbInfo['host'];
$this->mysql_user = $dbInfo['user'];
$this->mysql_pass = $dbInfo['pass'];
$this->openConnection();
return $this->get_link();
}
function openConnection(){
$this->databaseLink = mysql_connect($this->database, $this->mysql_user, $this->mysql_pass);
}
function get_link(){
return $this->databaseLink;
}
}
?>
./assets/db/dbSettings.php
<?php
$dbInfo = array(
'host' => "localhost",
'user' => "root",
'pass' => "password"
);
?>
It may be possible to increase performance by using a MySQL stored procedure as suggested by the "Geo-Distance-Search-with-MySQL" article posted above.
I have a database of ~17,000 places and the query execution time is 0.054 seconds.
Just in case you are lazy like me, here's a solution amalgamated from this and other answers on SO.
set #orig_lat=37.46;
set #orig_long=-122.25;
set #bounding_distance=1;
SELECT
*
,((ACOS(SIN(#orig_lat * PI() / 180) * SIN(`lat` * PI() / 180) + COS(#orig_lat * PI() / 180) * COS(`lat` * PI() / 180) * COS((#orig_long - `long`) * PI() / 180)) * 180 / PI()) * 60 * 1.1515) AS `distance`
FROM `cities`
WHERE
(
`lat` BETWEEN (#orig_lat - #bounding_distance) AND (#orig_lat + #bounding_distance)
AND `long` BETWEEN (#orig_long - #bounding_distance) AND (#orig_long + #bounding_distance)
)
ORDER BY `distance` ASC
limit 25;
Easy one ;)
SELECT * FROM `WAYPOINTS` W ORDER BY
ABS(ABS(W.`LATITUDE`-53.63) +
ABS(W.`LONGITUDE`-9.9)) ASC LIMIT 30;
Just replace the coordinates with your required ones. The values have to be stored as double. This ist a working MySQL 5.x example.
Cheers
Try this, it show the nearest points to provided coordinates (within 50 km). It works perfectly:
SELECT m.name,
m.lat, m.lon,
p.distance_unit
* DEGREES(ACOS(COS(RADIANS(p.latpoint))
* COS(RADIANS(m.lat))
* COS(RADIANS(p.longpoint) - RADIANS(m.lon))
+ SIN(RADIANS(p.latpoint))
* SIN(RADIANS(m.lat)))) AS distance_in_km
FROM <table_name> AS m
JOIN (
SELECT <userLat> AS latpoint, <userLon> AS longpoint,
50.0 AS radius, 111.045 AS distance_unit
) AS p ON 1=1
WHERE m.lat
BETWEEN p.latpoint - (p.radius / p.distance_unit)
AND p.latpoint + (p.radius / p.distance_unit)
AND m.lon BETWEEN p.longpoint - (p.radius / (p.distance_unit * COS(RADIANS(p.latpoint))))
AND p.longpoint + (p.radius / (p.distance_unit * COS(RADIANS(p.latpoint))))
ORDER BY distance_in_km
Just change <table_name>. <userLat> and <userLon>
You can read more about this solution here: http://www.plumislandmedia.net/mysql/haversine-mysql-nearest-loc/
You're looking for things like the haversine formula. See here as well.
There's other ones but this is the most commonly cited.
If you're looking for something even more robust, you might want to look at your databases GIS capabilities. They're capable of some cool things like telling you whether a point (City) appears within a given polygon (Region, Country, Continent).
Check this code based on the article Geo-Distance-Search-with-MySQL:
Example: find the 10 nearest hotels to my current location in a 10 miles radius:
#Please notice that (lat,lng) values mustn't be negatives to perform all calculations
set #my_lat=34.6087674878572;
set #my_lng=58.3783670308302;
set #dist=10; #10 miles radius
SELECT dest.id, dest.lat, dest.lng, 3956 * 2 * ASIN(SQRT(POWER(SIN((#my_lat -abs(dest.lat)) * pi()/180 / 2),2) + COS(#my_lat * pi()/180 ) * COS(abs(dest.lat) * pi()/180) * POWER(SIN((#my_lng - abs(dest.lng)) * pi()/180 / 2), 2))
) as distance
FROM hotel as dest
having distance < #dist
ORDER BY distance limit 10;
#Also notice that distance are expressed in terms of radius.
Find nearest Users to my:
Distance in meters
Based in Vincenty's formula
i have User table:
+----+-----------------------+---------+--------------+---------------+
| id | email | name | location_lat | location_long |
+----+-----------------------+---------+--------------+---------------+
| 13 | xxxxxx#xxxxxxxxxx.com | Isaac | 17.2675625 | -97.6802361 |
| 14 | xxxx#xxxxxxx.com.mx | Monse | 19.392702 | -99.172596 |
+----+-----------------------+---------+--------------+---------------+
sql:
-- my location: lat 19.391124 -99.165660
SELECT
(ATAN(
SQRT(
POW(COS(RADIANS(users.location_lat)) * SIN(RADIANS(users.location_long) - RADIANS(-99.165660)), 2) +
POW(COS(RADIANS(19.391124)) * SIN(RADIANS(users.location_lat)) -
SIN(RADIANS(19.391124)) * cos(RADIANS(users.location_lat)) * cos(RADIANS(users.location_long) - RADIANS(-99.165660)), 2)
)
,
SIN(RADIANS(19.391124)) *
SIN(RADIANS(users.location_lat)) +
COS(RADIANS(19.391124)) *
COS(RADIANS(users.location_lat)) *
COS(RADIANS(users.location_long) - RADIANS(-99.165660))
) * 6371000) as distance,
users.id
FROM users
ORDER BY distance ASC
radius of the earth : 6371000 ( in meters)
simpledb.execSQL("CREATE TABLE IF NOT EXISTS " + tablename + "(id INTEGER PRIMARY KEY AUTOINCREMENT,lat double,lng double,address varchar)");
simpledb.execSQL("insert into '" + tablename + "'(lat,lng,address)values('22.2891001','70.780154','craftbox');");
simpledb.execSQL("insert into '" + tablename + "'(lat,lng,address)values('22.2901396','70.7782428','kotecha');");//22.2904718 //70.7783906
simpledb.execSQL("insert into '" + tablename + "'(lat,lng,address)values('22.2863155','70.772108','kkv Hall');");
simpledb.execSQL("insert into '" + tablename + "'(lat,lng,address)values('22.275993','70.778076','nana mava');");
simpledb.execSQL("insert into '" + tablename + "'(lat,lng,address)values('22.2667148','70.7609386','Govani boys hostal');");
double curentlat=22.2667258; //22.2677258
double curentlong=70.76096826;//70.76096826
double curentlat1=curentlat+0.0010000;
double curentlat2=curentlat-0.0010000;
double curentlong1=curentlong+0.0010000;
double curentlong2=curentlong-0.0010000;
try{
Cursor c=simpledb.rawQuery("select * from '"+tablename+"' where (lat BETWEEN '"+curentlat2+"' and '"+curentlat1+"') or (lng BETWEEN '"+curentlong2+"' and '"+curentlong1+"')",null);
Log.d("SQL ", c.toString());
if(c.getCount()>0)
{
while (c.moveToNext())
{
double d=c.getDouble(1);
double d1=c.getDouble(2);
}
}
}
catch (Exception e)
{
e.printStackTrace();
}
It sounds like you want to do a nearest neighbour search with some bound on the distance. SQL does not support anything like this as far as I am aware and you would need to use an alternative data structure such as an R-tree or kd-tree.
MS SQL Edition here:
DECLARE #SLAT AS FLOAT
DECLARE #SLON AS FLOAT
SET #SLAT = 38.150785
SET #SLON = 27.360249
SELECT TOP 10 [LATITUDE], [LONGITUDE], SQRT(
POWER(69.1 * ([LATITUDE] - #SLAT), 2) +
POWER(69.1 * (#SLON - [LONGITUDE]) * COS([LATITUDE] / 57.3), 2)) AS distance
FROM [TABLE] ORDER BY 3
Sounds like you should just use PostGIS, SpatialLite, SQLServer2008, or Oracle Spatial. They can all answer this question for you with spatial SQL.
+----+-----------------------+---------+--------------+---------------+
| id | email | name | location_lat | location_long |
+----+-----------------------+---------+--------------+---------------+
| 7 | test#gmail.com | rembo | 23.0249256 | 72.5269697 |
| 25 | test1#gmail.com. | Rajnis | 23.0233221 | 72.5342112 |
+----+-----------------------+---------+--------------+---------------+
$lat = 23.02350629;
$long = 72.53230239;
DB::
SELECT
("
SELECT
*
FROM
(
SELECT
,
(
( ( acos( sin(( ". $ lat ." * pi() / 180)) * sin(( lat * pi() / 180)) + cos(( ". $ lat ." pi() / 180 )) * cos(( lat * pi() / 180)) * cos((( ". $ long ." - LONG) * pi() / 180))) ) * 180 / pi() ) * 60 * 1.1515 * 1.609344
)
as distance
FROM
users
)
users
WHERE
distance <= 2");
In extreme cases this approach fails, but for performance, I've skipped the trigonometry and simply calculated the diagonal squared.
Mysql query for search coordinates with distance limit and where condition
SELECT id, ( 3959 * acos( cos( radians('28.5850154') ) * cos( radians(latitude) ) * cos( radians( longitude ) - radians('77.07207489999999') ) + sin( radians('28.5850154') ) * sin( radians( latitude ) ) ) ) AS distance FROM `vendors` HAVING distance < 5;
This problem is not very hard at all, but it gets more complicated if you need to optimize it.
What I mean is, do you have 100 locations in your database or 100 million? It makes a big difference.
If the number of locations is small, get them out of SQL and into code by just doing ->
Select * from Location
Once you get them into code, calculate the distance between each lat/lon and your original with the Haversine formula and sort it.