How to lookup nearest location (Latitude / Longitude) in a join query - mysql

I have a table with a list of users, which looks something like;
Users.. ( id, username, lat, long )
I have a location table which looks like this:
Locations.. ( id, name, lat, lng, bound_ne_lat, bound_ne_lng, bound_sw_lat, bound_sw_lng
What I'm trying to do, is something like;
SELECT Users.*, Locations.name as NearestTown
FROM Users
INNER JOIN Locations ON ( .. lookup nearest town from Locations based on Users lat lng coords, return only the nearest result .. )
I did think about doing some kind of subquery, like;
INNER JOIN Locatios ON Locations.id IN (SELECT id FROM Locations ORDER BY (..distance query) desc limit 1
But then I found out I couldn't pass the users lat / lng into to the sub query for each result.
The current formula I do use however to calculate the distance between the 2 points is;
(3956 * 2 * ASIN(SQRT( POWER(SIN((#sourceLat - table.lookupLat) * pi()/180 / 2), 2) +COS(#sourceLat * pi()/180) * COS(table.lookupLat * pi()/180) * POWER(SIN((#sourceLng - table.lookupLng) * pi()/180 / 2), 2) ))) as Distance,
However, how could I use this in a subquery [if the best option] when I cannot pass in the #sourceLat and #sourceLng for each result .. ?
Any help, most appreciated.

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 can I run subqueries in mysql

I am trying to run subqueries from another table in a query
My Query is as follows:
SELECT *, (6371000 * acos(cos(radians(select point_oi.lng
from point_oi
where point_oi.name like '%Main Square%')
)
* cos(radians(restaurants.lat)) * cos(radians(restaurants.lng)
- radians(select point_oi.lng
from point_oi
where point_oi.name like '%Main Square%'
))
+ sin(radians(select point_oi.lng
from point_oi
where point_oi.name like '%Main Square%'))
* sin(radians(restaurants.lat)))) AS distance
FROM restaurants
HAVING distance < 500;
When I run the Query I get an error saying that there is an error near select.
I would like to use the nested select queries to get the lat and lng from another table rather than hardcoding the values.
How can I fix this.
Thank you for your help
You should not use subquery for retrieve point_poi lat, lnt if the suquery return more than a rows you have error ..
try use a proper join (in this case do the fatc you have not relation between point_poi and restaurants you could use cross join )
SELECT restaurants.*,
(6371000 * acos(cos(radians(point_oi.lng ))
* cos(radians(restaurants.lat)) * cos(radians(restaurants.lng)
- radians(point_oi.lng ))
+ sin(radians(point_oi.lng ))
* sin(radians(restaurants.lat)))) AS distance
FROM restaurants
CROSS JOIN point_oi
WHERE point_oi.name like '%Main Square%'
AND (6371000 * acos(cos(radians(point_oi.lng ))
* cos(radians(restaurants.lat)) * cos(radians(restaurants.lng)
- radians(point_oi.lng ))
+ sin(radians(point_oi.lng ))
* sin(radians(restaurants.lat)))) < 500;
Please try this query and let me know is your issue resolve.
SELECT t.*
,(6371000 * acos(cos(radians(SELECT point_oi.lng FROM point_oi WHERE point_oi.name LIKE '%Main Square%')) * cos(radians(restaurants.lat)) * cos(radians(restaurants.lng) - radians(SELECT point_oi.lng FROM point_oi WHERE point_oi.name LIKE '%Main Square%')) + sin(radians(SELECT point_oi.lng FROM point_oi WHERE point_oi.name LIKE '%Main Square%')) * sin(radians(restaurants.lat)))) AS distance
FROM restaurants As t
WHERE distance < 500;

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
;

MySQL - Filtering results by other tables

I have a procedure that has a main purpose of locating 9 users closest to you based on a longitude and latitude coordinate. However, I need to filter these results (and still return 9) based on values of other tables.
Here is the procedure that I'm currently using that doesn't have any filtering.
BEGIN
DECLARE lat FLOAT(11,8);
DECLARE lon FLOAT(11,8);
DECLARE dist INT(11);
SELECT latitude, longitude, search_radius
INTO lat, lon, dist
FROM accounts
INNER JOIN account_settings
ON accounts.account_id = account_settings.account_id
WHERE accounts.account_id = account_in;
SELECT account_id, first_name, bio, birthdate
, ( 3959 * acos( cos( radians(lat) ) * cos( radians(latitude) ) * cos( radians( longitude ) - radians(lon) ) + sin( radians(lat) ) * sin(radians( latitude )) ) )
AS distance
FROM `accounts`
WHERE account_id != account_in
HAVING distance < dist
ORDER BY distance ASC LIMIT 9;
END
However, I need to add some conditions to the select statement at the bottom, making it so it will still return 9 (if available) based on filtering from the following tables.
blocked_users
completed_test
failed_test
The three tables above have the same table structure of [ account_id, other_id ].
How would I make sure that none of the users returned in the last select statement are present in the other_id field of any of the tables listed, while matched with the corresponding account_id passed through the account_in variable.
I've been racking my brain for about 6 hours and I just can't seem to come up with a good conclusion. This query is much higher level (or I'm overthinking it a lot) than I'm used to dealing with. As i only have basic MySQL knowledge.

MYSQL Insert into select filtering fields

I have to insert a new row in table1 for each row found in table2.
The problem is that the select in table2 returns more fields than are needed to the insert, but are useful in where clause of select.
This query shows to a user all the shops around, based on their (shops) range (defined in the table stores)
SELECT destination.poi_id,
6371 *
2 *
ASIN(
SQRT(
POWER(SIN((use_lat - poi_lat) * PI()/180 / 2), 2) +
COS(use_lat * pi()/180) *
COS(poi_lat * pi()/180) *
POWER(SIN((use_lon - poi_lon) * PI()/180 / 2), 2)
)
) AS distance,
destination.poi_range AS range
FROM stores destination, users origin
WHERE origin.use_id=userid
AND destination.poi_lon BETWEEN lon1 AND lon2
AND destination.poi_lat BETWEEN lat1 AND lat2
HAVING distance <= range
ORDER BY distance;
Now I have to put these results in a table having this structure
user_id INTEGER
poi_id INTEGER
ins_date TIMESTAMP (CURRENT TIMESTAMP)
I don't know how to do it, can you help me?
INSERT INTO table (user_id, poi_id)
SELECT ... ? (too many fields in select)
Reading between the lines a bit but basically only include the columns you need in the insert IE:
INSERT INTO table (user_id, poi_id)
SELECT use_id,poi_id,NOW() FROM
(SELECT origin.use_id,destination.poi_id,6371 *
2 *
ASIN(
SQRT(
POWER(SIN((use_lat - poi_lat) * PI()/180 / 2), 2) +
COS(use_lat * pi()/180) *
COS(poi_lat * pi()/180) *
POWER(SIN((use_lon - poi_lon) * PI()/180 / 2), 2)
)
) as distance
FROM stores destination, users origin
WHERE origin.use_id=userid
AND destination.poi_lon BETWEEN lon1 AND lon2
AND destination.poi_lat BETWEEN lat1 AND lat2
HAVING distance <= range) sub1
ORDER BY distance;
You just need to add in your select user_id and remove distance and range because there are not in your table and actually you have to add ins_date because you dont define VALUE statement in your Insert. So your Select should be something like this
INSERT INTO table values (user_id, poi_id)
Select user_id, poi_id from FROM stores destination, users origin
WHERE origin.use_id=userid
AND destination.poi_lon BETWEEN lon1 AND lon2
AND destination.poi_lat BETWEEN lat1 AND lat2
HAVING distance <= range
ORDER BY distance;
if you need to add date in your table include this field within brackets and into select statement too