MySQL calculating distance (simple solution) - mysql

I have the next query for getting addresses within a given distance and given postal code. Distance is calculated, based upon longitude and latitude data.
In this example i have replaced the user-input for just values (lat=52.64, long=6.88 en desired distance=10km)
the query:
SELECT *,
ROUND( SQRT( POW( ( (69.1/1.61) * ('52.64' - latitude)), 2) + POW(( (53/1.61) * ('6.88' - longitude)), 2)), 1) AS distance
FROM lp_relations_addresses distance
WHERE distance < 10
ORDER BY `distance` DESC
gives unknown column distance as error message.
when leaving out the where clausule i get every record of the table including their calculated distance. In this case i have to fetch the whole table.
How do i get only the desired records to fetch??
Thanks in advance for any comment!

You can't reference an alias in the select clause from another part of the sql statement. You need to put the whole expression in your where clause:
WHERE
ROUND( SQRT( POW( ( (69.1/1.61) * ('52.64' - latitude)), 2)
+ POW(( (53/1.61) * ('6.88' - longitude)), 2)), 1) < 10
A cleaner solution would be to use a sub-query to generate the calculated data:
SELECT *, distance
FROM (
SELECT *,
ROUND( SQRT( POW( ( (69.1/1.61) * ('52.64' - latitude)), 2)
+ POW(( (53/1.61) * ('6.88' - longitude)), 2)), 1) AS distance
FROM lp_relations_addresses
) d
WHERE d.distance < 10
ORDER BY d.distance DESC
Demo: http://www.sqlize.com/q96p2mCwnJ

As mellamokb notes, you can't reference column aliases in the WHERE clause. You can, however, do it in a HAVING clause:
SELECT *,
ROUND( SQRT( POW( ( (69.1/1.61) * ('52.64' - latitude)), 2) +
POW(( (53/1.61) * ('6.88' - longitude)), 2)), 1) AS distance
FROM lp_relations_addresses
HAVING distance < 10
ORDER BY distance DESC
Ps. If you have lots of addresses, you might want to consider optimizing the query by ruling out some of them early. For example, with suitable indexes, the following version might be considerably faster:
SELECT *,
ROUND( SQRT( POW( ( (69.1/1.61) * ('52.64' - latitude)), 2) +
POW(( (53/1.61) * ('6.88' - longitude)), 2)), 1) AS distance
FROM lp_relations_addresses
WHERE latitude > '52.64' - 10 / (69.1/1.61)
AND latitude < '52.64' + 10 / (69.1/1.61)
AND longitude > '6.88' - 10 / (53/1.61)
AND longitude < '6.88' + 10 / (53/1.61)
HAVING distance < 10
ORDER BY distance DESC

You are aliasing the calculation as 'distance', but you are also aliasing table 'lp_relations_addresses' as 'distance'. Try giving them a different name like this:
SELECT *,
ROUND( SQRT( POW( ( (69.1/1.61) * ('52.64' - latitude)), 2) + POW(( (53/1.61) * ('6.88' - longitude)), 2)), 1) AS distance
FROM lp_relations_addresses addr
WHERE distance < 10
ORDER BY `distance` DESC

Related

How to reuse variable from SQL SELECT statement in WHERE clause

I need to take out values from database , based on distance from any given point and then order by distance ascending. Longitude and latitudes are saved in database as float field. Here is my query
SELECT
*,
#distance : = 6371 * 2 * asin(sqrt(POW(sin(({lat} - radians(address.latitude)) / 2), 2) + cos({lat}) * cos(radians(address.latitude)) * POW(sin(({lon} - radians(address.longitude)) / 2), 2)))
FROM
service,
provider,
address
WHERE
service.provider_id = provider.id
AND provider.address_id = address.id
AND provider.status = True
AND
(
6371 * 2 * asin(sqrt(POW(sin(({lat} - radians(address.latitude)) / 2), 2) + cos({lat}) * cos(radians(address.latitude)) * POW(sin(({lon} - radians(address.longitude)) / 2), 2)))
)
< 10
order by
#distance
I need to reuse the distance for condition like where #distance < 10
but I'm unable to reuse, it returns empty list. whereas #distance in ORDER BY is working fine. How can i reuse variable in where clause ?
Assuming you do not need #DISTANCE outside the select, here's an approach to try:
SELECT *,
6371 * 2 * asin(sqrt(POW(sin(({lat} - radians(address.latitude)) / 2), 2) + cos({lat}) * cos(radians(address.latitude)) * POW(sin(({lon} - radians(address.longitude)) / 2), 2))) AS DISTANCE
FROM service,
provider,
address
WHERE
service.provider_id = provider.id
AND provider.address_id = address.id
AND provider.status = True
HAVING
DISTANCE < 10
ORDER BY
DISTANCE
Note that a I have given the calculation an alias DISTANCE(see far right of select) and used HAVING rather than WHERE for the reference to the alias.
Avoid using MySQL variables; they are obsolete in newer versions of MySQL. Also, it's better to use modern syntax join (SQL-92) instead of old joins.
Anyway, I think the following query produces what you want:
select *
from (
SELECT
*,
6371 * 2 * asin(sqrt(POW(sin(({lat} - radians(a.latitude)) / 2), 2)
+ cos({lat}) * cos(radians(a.latitude)) * POW(sin(({lon}
- radians(a.longitude)) / 2), 2))) as distance
FROM service s
JOIN provider p ON s.provider_id = p.id
JOIN address a ON p.address_id = a.id
WHERE p.status = True
) x
WHERE distance < 10
ORDER BY distance

How to search for events in 5 km radius

I'm trying to select events in a 5 km radius and order them by likes and from oldest to newest. I know how to select events and order them by likes, but I can't figure this one out. The problem maybe is that the location is stored in a different table.
Database structure:
event plusone (like) location
id_event id_plusone id_location
location_id event_id lat
... user_id lon
... city_id
full_name
....
The SQL statement I'm trying:
SELECT 3956 * 2 * ASIN(
SQRT(POWER(SIN((50.127946 - abs(l.lat)) * pi()/180 / 2),2) + COS(50.127946 * pi()/180 ) *
COS(abs(l.lat) * pi()/180) * POWER(SIN((14.4882912 - l.lon) * pi()/180 / 2), 2) )) AS distance,e.*,COUNT( p.id_plusone ) AS
likes
FROM
event e,location l
LEFT JOIN plusone p ON p.event_id = e.id_event
WHERE
e.location_id = l.id_location AND distance<=5 GROUP BY e.id_event
ORDER BY e.timestamp DESC,likes DESC
The huge column at the beginning is calculation of distance of the event from user position using latitude and longitude.
This is what I get from the SQL debugger:
#1054 - Unknown column 'distance' in 'where clause'
Does anybody know how to do this?
Is hard to track where the error is, you should try to solve the distance part first.
SELECT
event_id,
3956 * 2 * ASIN(SQRT( POWER(SIN((50.127946 - abs(l.lat)) * pi()/180 / 2),2)
+ COS(50.127946 * pi()/180 ) * COS(abs(l.lat) * pi()/180) * POWER(SIN((14.4882912 – l.lon) * pi()/180 / 2), 2) )) AS distance,
FROM event e
JOIN location l
WHERE distance <= 5
And then join to back to the event table
SELECT e.*, count(p.id_plusone)
FROM event e
LEFT JOIN plusone p
ON p.event_id = e.id_event
JOIN ( Previous Distance Query) d
ON e.event_id = d.event_id
GROUP BY e.id_event
ORDER BY e.timestamp DESC, likes DESC
and btw, this group_by will bring you event_id plus random values for e.* so be carefull
$latitudeD= latitude;
$longitudeD = longitude;
SELECT event,
SQRT(
POW(69.1 * (coordX - $latitudeD), 2) +
POW(69.1 * ($longitudeD - coordY) * COS(coordX / 57.3), 2)) AS distance
FROM db.table WHERE SQRT(
POW(69.1 * (coordX - $latitudeD), 2) +
POW(69.1 * ($longitudeD - coordY) * COS(coordX / 57.3), 2)) < 50 ORDER BY distance
;

SQL query and calculations; formatting query correctly to select only id

I have a query that selects all locations within a 30 mile radius of a GPS pt. I want to get the id's of those locations, but it's also returning the distance from the center pt.
Is there anyway to perform the distance calculation without returning it?
Query:
SELECT id, 3956 * 2 * ASIN(SQRT(
POWER(SIN((34.1844709 - abs(dest.lat)) * pi()/180 / 2),
2) + COS(37.7749295 * pi()/180 ) * COS(abs(dest.lat) *
pi()/180) * POWER(SIN((-118.131809 - dest.lng) *
pi()/180 / 2), 2) )) as distance
FROM location dest
having distance < 30
ORDER by distance
LIMIT 30
Output:
---------------------------
id | distance
---------------------------
1 | 2.310
2 | 2.356
17 | 4.298
Query based off:
http://www.notaires.fr/sites/default/files/geo_searchjkkjkj_0.pdf
Can you just do another select on this?
Select id
From (SELECT id, 3956 * 2 * ASIN(SQRT(
POWER(SIN((34.1844709 - abs(dest.lat)) * pi()/180 / 2),
2) + COS(37.7749295 * pi()/180 ) * COS(abs(dest.lat) *
pi()/180) * POWER(SIN((-118.131809 - dest.lng) *
pi()/180 / 2), 2) )) as distance
FROM location dest
having distance < 30
ORDER by distance
LIMIT 30) dst

MySQL distance calculating

I currently run a query that returns a distance column displaying the distance in miles from a given point
SELECT user_id,
SQRT(
POW(69.1 * (lat_col - 60.454509), 2) +
POW(69.1 * (9.667969 - lng_col) * COS(lat_col / 57.3), 2)
) AS distance
FROM users ORDER BY distance ASC
Returns
user_id | distance
-------------------
1 27
56 29
8 32
32 45
4 56
6 64
The problem is that this distance value is in miles and i need it to be in kilometres. I can do this in php but I would prefer to keep things simple, fast and clean by using the current SQL I have.
1 mile = 1.609344 kilometres so we can just alter the distance:
SELECT user_id,
SQRT(
POW(69.1 * (lat_col - 60.454509), 2) +
POW(69.1 * (9.667969 - lng_col) * COS(lat_col / 57.3), 2)
) * 1.609344 AS distance
FROM users ORDER BY distance ASC
A degree longitude is average 111 km it depends where.
1 Mile = 1.60934 Kilometer. So you just need to multiply your distance calculation with 1.60934
SQRT(
POW(69.1 * (lat_col - 60.454509), 2) +
POW(69.1 * (9.667969 - lng_col) * COS(lat_col / 57.3), 2)
) * 1.60934 AS distance

Zipcode Radius Search with POINT Column

I have a MySQL Routine that is getting records within a 50 mile radius when passed Latitude and Longitude via utilizing the Haversin equation.
While this works great, and is pretty speedy (considering it's searching through 82k records), I am thinking that I can get better performance by creating a similar procedure utilizing a POINT column.
So, in my table I created an extra column called Location, gave it a datatype of POINT, updated my data to pass lat & lon to the Location column. Data is valid, and is fine., and added a Spatial Index
The question is, how can I convert the following query to use the Location column, instead of lat and lon columns.
SET #LAT := '37.953';
SET #LON := '-105.688';
SELECT DISTINCT
BPZ.`store_id`,
3956 * 2 * ASIN(SQRT(POWER(SIN((#LAT - abs(Z.`lat`)) * pi()/180 / 2),2) + COS(#LAT * pi()/180 ) * COS(abs(Z.`lat`) * pi()/180) * POWER(SIN((#LON - Z.`lon`) * pi()/180 / 2), 2))) as distance,
c.`name`,c.`address`,c.`city`,c.`state`,c.`phone`,c.`zip`,c.`premise_type`
FROM
`zip_codes` as Z,
`brand_product_zip` as BPZ
LEFT JOIN `customers` c ON c.`store_id` = BPZ.`store_id`
WHERE
BPZ.`zip` = Z.`zip`
AND
3956 * 2 * ASIN(SQRT(POWER(SIN((#LAT - abs(Z.`lat`)) * pi()/180 / 2),2) + COS(#LAT * pi()/180 ) * COS(abs(Z.`lat`) * pi()/180) * POWER(SIN((#LON - Z.`lon`) * pi()/180 / 2), 2))) <= 50
ORDER BY
distance LIMIT 20
I understand that this has been asked before, however, everything I see points to calculations based on lat and lon and not the POINT column
Updated Code:
SET #lat = 41.92;
SET #lon = -72.65;
SET #kmRange = 80.4672; -- = 50 Miles
SELECT *, (3956 * 2 * ASIN(SQRT(POWER(SIN((#lat - abs(`lat`)) * pi()/180 / 2),2) + COS(#lat * pi()/180 ) * COS(abs(`lat`) * pi()/180) * POWER(SIN((lon - `lon`) * pi()/180 / 2), 2)))) as distance
FROM `zip_codes`
WHERE MBRContains(LineString(Point(#lat + #kmRange / 111.1, #lon + #kmRange / (111.1 / COS(RADIANS(#lat)))), Point(#lat - #kmRange / 111.1, #lon - #kmRange / (111.1 / COS(RADIANS(#lat))))), `Location`)
Order By distance
LIMIT 20
Have you looked into hilbert curves solutions? A spatial index doesn't deliver the exact solution? . With a mysql spatial index you can use mbrcontains:
CREATE TABLE lastcrawl (id INT NOT NULL PRIMARY KEY, pnt POINT NOT NULL) ENGINE=MyISAM;
INSERT
INTO lastcrawl
VALUES (1, POINT(40, -100));
SET #lat = 40;
SET #lon = -100;
SELECT *
FROM lastcrawl
WHERE MBRContains
(
LineString
(
Point
(
#lat + 10 / 111.1,
#lon + 10 / ( 111.1 / COS(RADIANS(#lat)))
),
Point (
#lat - 10 / 111.1,
#lon - 10 / ( 111.1 / COS(RADIANS(#lat)))
)
),
pnt
);
Look here: MySQL - selecting near a spatial point.
Here: http://www.drdobbs.com/database/space-filling-curves-in-geospatial-appli/184410998
The article Nearest-location finder for MySQL explains in detail various options, and the best choice for use with the Spatial Extensions starting with MySQL 5.6.
From the article, this sample query lists zip codes within a 50 mile radius from given coordinates (42.81, -70.81):
SELECT zip, primary_city,
latitude, longitude, distance_in_mi
FROM (
SELECT zip, primary_city, latitude, longitude,r,
69.0 * DEGREES(ACOS(COS(RADIANS(latpoint))
* COS(RADIANS(latitude))
* COS(RADIANS(longpoint) - RADIANS(longitude))
+ SIN(RADIANS(latpoint))
* SIN(RADIANS(latitude)))) AS distance_in_mi
FROM zip
JOIN (
SELECT 42.81 AS latpoint, -70.81 AS longpoint, 50.0 AS r
) AS p
WHERE latitude
BETWEEN latpoint - (r / 69)
AND latpoint + (r / 69)
AND longitude
BETWEEN longpoint - (r / (69 * COS(RADIANS(latpoint))))
AND longpoint + (r / (69 * COS(RADIANS(latpoint))))
) d
WHERE distance_in_mi <= r
ORDER BY distance_in_mi;