Mysql JOIN multiple tables with latitude and longitude - mysql

I'm trying to display advertisements relevant to data of the user.
Here, data is Latitude and longitude.
ADS table: ad to display with ad name, ad text, latitude and longitude
Tables with data of the user:
TABLE1: user id + latitude and longitude,
TABLE2: user id + latitude and longitude,
TABLE3: user id + latitude and longitude,
TABLE4: user id + latitude and longitude
The purpose is to display an ad, when the latitude and longitude of the ad matches the latitude and longtitude of one or more lines in the 4 tables in a range of 10km and limit by 3 results.
It's working when linking one ad with one table in range of 10km, but doesn't with more tables.
- I'm not comfortable with OUTER JOINS -
My query looks like this and I'm pretty sure I'm going wrong...
SELECT ADS.name, ADS.text,
( 6371 * acos( cos( radians(TABLE1.latitude) )
* cos( radians( ADS.latitude ) )
* cos( radians( ADS.longitude )
- radians(TABLE1.longitude) )
+ sin( radians(TABLE1.latitude) )
* sin( radians( ADS.latitude ) ) ) )
AS check1,
( 6371 * acos( cos( radians(TABLE2.latitude) )
* cos( radians( ADS.latitude ) )
* cos( radians( ADS.longitude )
- radians(TABLE2.longitude) )
+ sin( radians(TABLE2.latitude) )
* sin( radians( ADS.latitude ) ) ) )
AS check2,
( 6371 * acos( cos( radians(TABLE3.latitude) )
* cos( radians( ADS.latitude ) )
* cos( radians( ADS.longitude )
- radians(TABLE3.longitude) )
+ sin( radians(TABLE3.latitude) )
* sin( radians( ADS.latitude ) ) ) )
AS check3,
( 6371 * acos( cos( radians(TABLE4.latitude) )
* cos( radians( ADS.latitude ) )
* cos( radians( ADS.longitude )
- radians(TABLE4.longitude) )
+ sin( radians(TABLE4.latitude) )
* sin( radians( ADS.latitude ) ) ) )
AS check4
FROM ADS
RIGHT OUTER JOIN TABLE1
ON TABLE1.user = ?
RIGHT OUTER JOIN TABLE2
ON TABLE2.user = ?
RIGHT OUTER JOIN TABLE3
ON TABLE3.user = ?
RIGHT OUTER JOIN TABLE4
ON TABLE4.user = ?
HAVING check1 < 10 OR check2 < 10 OR check3 < 10 OR check4 < 10
LIMIT 0,3

If user isn't unique in TABLE1 (or any of the TABLEn), then there's a potential to return multiple copies of the same row from ADS.
With the query the way it is, if the specified user isn't found in TABLE4, then the query won't return any rows. I suspect what you meant was a LEFT JOIN, with ADS as the driving table, but that's just a guess. We don't know what those tables contain, why are there four of them, etc.)
If there's a reason you are using a RIGHT JOIN, and if the query with one table is working for you...
and if there is a small number of rows in each of TABLE1, TABLE2, TABLE3, TABLE4 for a specified user...
You could concatenate the results of queries of those tables into single derived table, and then join to the derived table. As an example:
SELECT ADS.name, ADS.text,
( 6371 * acos( cos( radians(t.latitude) )
* cos( radians( ADS.latitude ) )
* cos( radians( ADS.longitude )
- radians(t.longitude) )
+ sin( radians(t.latitude) )
* sin( radians( ADS.latitude ) ) )
) AS check1
FROM ADS
RIGHT
JOIN ( SELECT TABLE1.latitude, TABLE1.longitude
FROM TABLE1 WHERE TABLE1.user = ?
UNION ALL
SELECT TABLE2.latitude, TABLE2.longitude
FROM TABLE2 WHERE TABLE2.user = ?
UNION ALL
SELECT TABLE3.latitude, TABLE3.longitude
FROM TABLE3 WHERE TABLE3.user = ?
UNION ALL
SELECT TABLE4.latitude, TABLE4.longitude
FROM TABLE4 WHERE TABLE4.user = ?
) t
HAVING check1 < 10
LIMIT 3

This was finaly what I needed:
$reponse = $bdd->prepare('
SELECT * FROM
(
SELECT ADS.id AS id, ADS.name, ADS.text,
( 6371 * acos( cos( radians(t.latitude) )
* cos( radians(ADS.latitude ) )
* cos( radians( ADS.longitude )
- radians(t.longitude) )
+ sin( radians(t.latitude) )
* sin( radians( ADS.latitude ) ) ) )
AS distance
FROM ADS
RIGHT
JOIN ( SELECT TABLE1.latitude, TABLE1.longitude
FROM TABLE1 WHERE TABLE1.user = :user
UNION ALL
SELECT TABLE2.latitude, TABLE2.longitude
FROM TABLE2 WHERE TABLE2.user = :user
UNION ALL
SELECT TABLE3.latitude, TABLE3.longitude
FROM TABLE3 WHERE TABLE3.user = :user
UNION ALL
SELECT TABLE4.latitude, TABLE4.longitude
FROM TABLE4 WHERE TABLE4.user = :user
) t
ON 1
HAVING distance < 10
) req GROUP BY id LIMIT 0,3
');
$reponse->execute(array('user' => $_SESSION['id']));

Related

Join two tables subquery

I'm trying to get * from the users2 table where the user's location is within the given radius.
The location query works fine on the user_location2 table.
SELECT uid, ( 3959 * acos( cos( radians(28.247800068217) ) * cos( radians( `lat` ) ) * cos( radians( `lon` ) - radians(-80.726205977101) )
+ sin( radians(28.247800068217) ) * sin( radians( `lat` ) ) ) )
AS distance FROM user_location2
HAVING distance <= 25 ORDER BY time_stamp
and the inner join works fine without the location subquery
SELECT *
FROM users2
LEFT JOIN user_location2
ON user_location2.uid = users2.id
I'm just having trouble combining the two. Here's my current query that just is returning all rows, so I'm obviously doing something wrong.
SELECT *
FROM users2
LEFT JOIN user_location2
ON user_location2.uid = users2.id
WHERE EXISTS (SELECT NULL, ( 3959 * acos( cos( radians(26.247800068217) ) * cos( radians( `lat` ) ) * cos( radians( `lon` ) - radians(-89.726205977101) ) + sin( radians(26.247800068217) ) * sin( radians( `lat` ) ) ) )
AS distance FROM user_location2
HAVING distance <= 5 ORDER BY time_stamp)
Edit included
I'm hoping to add in a 3rd table (user_like) to eliminate a lot of possible rows that shouldn't be included in the result.
Let's say the script is running for user_id = 88
So basically users 89, 90 and 91 would fall under the location radius, but wouldn't be included in the result because user 88 already liked them.
Try this...
SELECT users2.*
FROM users2
LEFT JOIN user_location2
ON user_location2.uid = users2.id
WHERE ( 3959 * acos( cos( radians(28.247800068217) ) * cos( radians( `lat` ) ) * cos( radians( `lon` ) - radians(-80.726205977101) )
+ sin( radians(28.247800068217) ) * sin( radians( `lat` ) ) ) ) < = 5
ORDER BY time_stamp
The join is fine between the two tables. The calculated column has been added in the select clause and the where clause because the filter requires it there. It would be easier to put that through a view so that if you need to change it, it can be done in one place.
EDIT: Removed the calculation from SELECT because I believe you don't need to see that. Just left it in the WHERE clause since it needs to be filtered on.
You can try this :
SELECT *
FROM users2 A
LEFT JOIN user_location2 B
ON B.uid = A.id
WHERE ( 3959 * acos( cos( radians(26.247800068217) ) * cos( radians( `B.lat` ) ) * cos( radians( `B.lon` ) - radians(-89.726205977101) ) + sin( radians(26.247800068217) ) * sin( radians( `B.lat` ) ) ) ) > 25;

Merging 2 sql queries into a single one

I have two sql queries which when run independent produces the correct results
Query 1
SELECT id,
(6371 * acos( cos( radians(9.977364864079215) ) * cos( radians( latitude ) ) * cos( radians( longitude ) - radians(76.58620953448485) ) + sin( radians(9.977364864079215) ) * sin( radians( latitude ) ) ) )
AS distance
FROM geodata HAVING distance < 20
ORDER BY distance
LIMIT 0 , 20;
Query 2
SELECT DISTINCT e.* FROM schools e
WHERE (
(e.type = 'preprimary')
)
AND(
e.title LIKE '%government%'
)
LIMIT 0, 10
I want to merge the first query with the second one, so that it should return all "preprimary" type schools with title like "government" located within 20KM radius and the result needs to be ordered by the distance.
How can I merge the two queries? I tried using JOINING the geodata table on the school table. But I dont know the remaining. Sorry, if this is a silly question. I am pretty new to SQL world.
SELECT DISTINCT school.* FROM
( SELECT geodata.id,
(6371 * acos( cos( radians(9.977364864079215) ) * cos( radians( latitude ) ) * cos( radians( longitude ) - radians(76.58620953448485) ) + sin( radians(9.977364864079215) ) * sin( radians( latitude ) ) ) )
AS distance ,school.*
FROM geodata LEFT JOIN school on geodata.id=school.id
WHERE
(school.type = 'preprimary')
AND(
school.title LIKE '%government%'
)
AND school.id IS NOT NULL
HAVING distance < 20 )x
ORDER BY x.distance
LIMIT 0 , 10;
Try this:
SELECT *
From (
SELECT DISTINCT e.* ,
(6371 * acos( cos( radians(9.977364864079215) ) * cos( radians( latitude ) ) * cos( radians( longitude ) - radians(76.58620953448485) ) + sin( radians(9.977364864079215) ) * sin( radians( latitude ) ) )
) as distance
FROM schools e
LEFT JOIN geodata g ON e.id=g.id
WHERE (e.type = 'preprimary')
AND ( e.title LIKE '%government%' )
) as s
Where s.distance < 20
Order by s.distance

Search by alias without showing the alias

I have a table of categories and a table of items.
Each item has latitude and longitude to allow me to search by distance.
What I want to do is show each category and how many items are in that category, within a distance chosen.
E.g. Show all TVs in Electronics category within 1 mile of my own latitude and longitude.
Here's what I'm trying but I cannot have two columns within an alias, obviously, and am wondering if there is a better way to do this?
Here is a SQL fiddle
Here's the query:
SELECT *, ( SELECT count(*),( 3959 * acos( cos( radians(52.993252) )
* cos( radians( latitude ) )
* cos( radians( longitude ) - radians(-0.412470) )
+ sin( radians(52.993252) )
* sin( radians( latitude ) ) ) ) AS distance
FROM items
WHERE category = category_id group by item_id
HAVING distance < 1 ) AS howmanyCat,
( SELECT name FROM categories WHERE category_id = c.parent ) AS parname
FROM categories c ORDER BY category_id, parent
First, start with the distance calculation for each item, then join in the category information and aggregate and filter
select c.*, count(i.item_id) as numitems
from category c left outer join
(SELECT i.*, ( 3959 * acos( cos( radians(52.993252) ) * cos( radians( latitude ) )
* cos( radians( longitude ) - radians(-0.412470) ) + sin( radians(52.993252) )
* sin( radians( latitude ) ) )
) AS distance
FROM items i
) i
on c.category_id = i.category_id and distance < 1
group by category_id;
Is this what you're looking for:
SELECT categories.name, count(items.item_id) as cnt
FROM items
JOIN categories
ON categories.category_id=items.category
WHERE ( 3959 * acos( cos( radians(52.993252) )
* cos( radians( latitude ) )
* cos( radians( longitude ) - radians(-0.412470) )
+ sin( radians(52.993252) )
* sin( radians( latitude ) ) ) ) < 1
GROUP BY categories.category_id;
this gives:
Tvs | 1
You can put the expression for computing the distance inside a nested SELECT, and then join the results to the categories table, like this:
SELECT COUNT(*), cc.name FROM (
SELECT
i.item_id
, c.category_id
, ( 3959 * acos( cos( radians(52.993252) )
* cos( radians( latitude ) )
* cos( radians( longitude ) - radians(-0.412470) )
+ sin( radians(52.993252) )
* sin( radians( latitude ) ) ) ) AS distance
FROM items i
JOIN categories c ON c.category_id = i.category
) raw
JOIN categories cc ON raw.category_id = cc.category_id AND raw.distance < 1
GROUP BY cc.name
The nested query pairs up items and categories, and adds the calculated distance column. The outer query then filters the rows by distance, and groups them by category to produce the desired output:
COUNT(*) NAME
-------- ----
1 TVs
Demo on sqlfiddle.

combine 2 select queries in mysql

I have 2 select statements:
timestamp of emp getting awards for specific emp id
SELECT * FROM user_table,employeetable,awards where user_table.empid=employeetable.empid AND user_table.empid=awards.empid AND user_table.empid=123 ORDER BY timestamp DESC
All employees staying around 25 miles from the current loc:current location: lat =37 lng=-122
SELECT * ( 3959 * acos( cos( radians(37) ) * cos( radians( lat ) ) * cos( radians( lng ) - radians(-122) )+ sin( radians(37) ) * sin( radians( lat ) ) ) ) AS distance FROM user_table,employeetable,awards where user_table.empid=employeetable.empid AND user_table.empid=awards.empid HAVING distance < 25 ORDER BY distance;
How do I combine both and ORDER BY timestamp ?btw both have field timestamp.
1.has specific user
2.all users within specific radius
I really appreciate any help.Thanks in Advance.
You can combine the two queries into a single query, just using logic in the where clause (which this has turned into a having clause:
select *, ( 3959 * acos( cos( radians(37) ) * cos( radians( lat ) ) * cos( radians( lng ) - radians(-122) )+ sin( radians(37) ) * sin( radians( lat ) ) ) ) as distance
from user u join
employee e
on u.empid = e.empid join
awards a
on u.empid = a.empid
having empid = 123 or distance < 25;
This uses having instead of where so the distance column alias can be used instead of the formula.

MySQL: Combining two queries into one row result

First part of the query:
SET #centerLat = '48.531157';
SET #centerLng = '-123.782959';
SELECT user_id, lat, lng, ( 3959 * acos( cos( radians( #centerLat ) ) * cos( radians( lat ) ) * cos( radians( lng ) - radians(#centerLng) ) + sin( radians( #centerLat ) ) * sin( radians( lat ) ) ) ) AS distance FROM bid_userloc HAVING distance < 25 ORDER BY distance LIMIT 0 , 20
Second aspect is taking the user_id and grabbing a bunch of information from the USERS table
I'm still learning what JOIN even means and I don't quite understand how it all works best...
you may try this
SELECT user_id, lat, lng, ( 3959 * acos( cos( radians( #centerLat ) )
* cos( radians( lat ) ) * cos( radians( lng ) - radians(#centerLng) )
+ sin( radians( #centerLat ) ) * sin( radians( lat ) ) ) )
AS distance,columnsfromuserstable FROM bid_userloc bid
inner join users us on bid.user_id=us.user_id
HAVING distance < 25
ORDER BY distance LIMIT 0 , 20
You can try something like this:
select * from users where user_id in (select user_id from(
SELECT user_id, lat, lng, ( 3959 * acos( cos( radians( #centerLat ) ) * cos( radians( lat ) ) * cos( radians( lng ) - radians(#centerLng) ) + sin( radians( #centerLat ) ) * sin( radians( lat ) ) ) ) AS distance FROM bid_userloc HAVING distance < 25 ORDER BY distance LIMIT 0 , 20
));
Variant with JOIN -
SET #centerLat = '48.531157';
SET #centerLng = '-123.782959';
SELECT
t1.user_id, t1.lat, t1.lng,
(3959 * ACOS(COS(RADIANS(#centerLat)) * COS(RADIANS(t1.lat)) * COS(RADIANS(t1.lng) - RADIANS(#centerLng)) + SIN(RADIANS(#centerLat)) * SIN(RADIANS(t1.lat)))) distance,
t2.*
FROM
bid_userloc t1
JOIN users t2
ON t1.user_id = t2.user_id
HAVING
distance < 25
ORDER BY
distance
LIMIT
0, 20;