Search by alias without showing the alias - mysql

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.

Related

Mysql JOIN multiple tables with latitude and longitude

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']));

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

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 distinct or group by in combination with having not giving a result when result is a single row

It seems that my query is not exactly doing what I want. The query gets a result aslong as the result is 2 or more rows. When I get a single row the query is not getting any result.
In the SELECT I can do DISTINCT (ct.name) but this gives the same problem as the group by.
SELECT
ct.name,
( 3959 * acos(cos(radians(52.779716)) * cos(radians( com.gps_lat )) * cos(radians( com.gps_lon ) -
radians(21.84803)) + sin( radians(52.779716) ) * sin( radians( com.gps_lat )))) as distance
FROM cuisine_types as ct
Left joining company to check if a company is attached to the cuisine_type
LEFT JOIN company AS com ON (com.cuisine_type_id = ct.id)
Here I'm grouping the results so no Cuisine Type appears twice.
this only seems to work when the result is 2 or more rows...
GROUP BY ct.name
Here I'm checking if the distance of the company is within the users preferenced search radius
HAVING distance < 20;
for example if I had 'Fastfood', 'Vegan', and 'Healthy' as Cuisine Types, I only want one of each Cuisine Types no matter how many companies in the search distance are related to that Cuisine Type. So I filter the double Cuisine Types away using the GROUP BY I hope this helps with understanding my approach in this query.
NOTE: There is only one Cuisine Type attached to a company.
Full sql query without comments down here
SELECT ct.name, ( 3959 * acos( cos( radians(52.779716) ) * cos(
radians( com.gps_lat ) ) * cos( radians( com.gps_lon ) -
radians(21.84803) ) + sin( radians(52.779716) ) * sin( radians(
com.gps_lat ) ) ) ) as distance FROM cuisine_types as ct LEFT JOIN
company AS com ON (com.cuisine_type_id = ct.id) GROUP BY ct.name
HAVING distance < 20;
Try this:
SELECT
ct.name,
min( ( 3959 * acos( cos( radians(52.779716) ) * cos( radians( com.gps_lat ) ) * cos( radians( com.gps_lon ) - radians(21.84803) ) + sin( radians(52.779716) ) * sin( radians( com.gps_lat ) ) ) ) ) as distance
FROM
cuisine_types as ct
LEFT JOIN company AS com ON (com.cuisine_type_id = ct.id)
GROUP BY
ct.name
HAVING
distance < 20;

Getting latitude and longitude from a postcode table (MySql) and joining to another table

I have a charities table with fields: charity, postcode
and a postcodes table with fields: postcode, lat, lng
I want to POST a postcode from a web page and find the nearest charities
I'm something of a mysql beginner so I'm a bit lost, but I've been trying various ideas with joins and sub queries none of which work (I either get syntax errors or 'Operand should contain 1 column' with variations on the code below) I've got
Select charity,postcode,
(
(Select lat as lat2, lng as lng2
from postcodes
where postcode='WN8'
)
3959 * acos( cos( radians(lat2) ) * cos( radians( lat ) ) *
cos( radians( lng ) - radians(lng2) ) +
sin( radians(lat2) ) * sin( radians( lat ) ) )
)
AS distance
FROM postcodes
JOIN Charities on charities.postcode=postcodes.postcode
HAVING distance < 30 ORDER BY distance LIMIT 0 , 30;
I've seen lots of examples on here where lat2 and lng2 are obtained from posted values but not from a table in the db.
p.s 'where postcode='WN8' in the example is just for testing
Not sure what error you are getting with the above SQL.
However try this minor tweak and let us know what errors you get
SELECT charity, postcode,
(3959 * acos( cos( radians(CustPostcode.lat) ) * cos( radians( postcodes.lat ) ) *
cos( radians( postcodes.lng ) - radians(CustPostcode.lng) ) +
sin( radians(CustPostcode.lat) ) * sin( radians( postcodes.lat ) ) )
) AS distance
FROM postcodes
INNER JOIN Charities ON charities.postcode=postcodes.postcode
CROSS JOIN (SELECT lat, lng FROM postcodes WHERE postcode='WN8') CustPostcode
HAVING distance < 30
ORDER BY distance
LIMIT 0 , 30;
If you want to know the nearest 30 postcodes and the distances from each charity then something like this would do the job (not tested so excuse any typos).
SELECT charity, Charities.postcode, Postcodes.postcode, PostcodeDistance.distance
FROM Charities
CROSS JOIN Postcodes
INNER JOIN (SELECT PC1.postcode AS postcode1, PC2.postcode AS postcode2, (3959 * acos( cos( radians(PC1.lat) ) * cos( radians( PC2.lat ) ) *
cos( radians( PC2.lng ) - radians(PC1.lng) ) +
sin( radians(PC1.lat) ) * sin( radians( PC2.lat ) ) )
) AS distance
FROM postcodes PC1
CROSS JOIN postcodes PC2) PostcodeDistance
ON Charities.postcode = PostcodeDistance.postcode1
AND Postcodes.postcode = PostcodeDistance.postcode2
HAVING distance < 30
ORDER BY distance
LIMIT 0 , 30;
This should find you the charities within 30 miles
SELECT charity, Charities.postcode, PostcodeDistance.distance
FROM Charities
INNER JOIN (
SELECT PC2.postcode AS postcode2, (3959 * acos( cos( radians(PC1.lat) ) * cos( radians( PC2.lat ) ) *
cos( radians( PC2.lng ) - radians(PC1.lng) ) +
sin( radians(PC1.lat) ) * sin( radians( PC2.lat ) ) )
) AS distance
FROM postcodes PC1
CROSS JOIN postcodes PC2
WHERE PC1.postcode='WN8'
) PostcodeDistance
ON Charities.postcode = PostcodeDistance.postcode2
WHERE PostcodeDistance.distance < 30
ORDER BY PostcodeDistance.distance
LIMIT 0 , 30;