using ORDER BY in a mysql statement and also grouping the results - mysql

I'm doing a query on a set of users that returns the users within a specific radius. The users belong to 1 of 3 different types of user groups. I need to sort the return list by showing the users from group 1 (sorted by distance) and then the users from group 2 & 3 combined (sorted by distance). I could easily use an ORDER BY clause, but I don't know how to be sure that groups 2 & 3 are combined together in the results.
Here is the statement that returns the users first by group 1, then 2, then 3 and sorted by distance inside those groups. I need to return group 1, then 2&3, sorted by distance.
Also I'm not sure if I'm using the INNER JOIN properly, should this be a LEFT JOIN?
SELECT
SQL_CALC_FOUND_ROWS
a.*,
b.group_id,
ROUND(( 3959 * acos( cos( radians( $lat ) ) * cos( radians( a.latitude ) ) * cos( radians( a.longitude ) - radians( $lon ) ) + sin( radians( $lat ) ) * sin( radians( a.latitude ) ) ) ), 1) AS distance
FROM `users` AS a
INNER JOIN `user_group_map` AS b
ON a.`id` = b.`user_id`
HAVING distance <= $radius
ORDER BY
b.`group_id` DESC,
distance ASC

You can use an ORDER BY CASE to conditionally apply the order forcing group 1 first. I think this should do the job:
ORDER BY
CASE WHEN b.group_id = 1 THEN 0 ELSE 1 END,
distance ASC
The CASE statement returns 0 for group_id = 1 and 1 for any other value of group_id. The 0 sorts ahead of the 1, and these two subgroups (0 & 1) are then subordered by distance ASC so group_id 2 & 3 sort together.

Related

Query to get nearest distance record

I have following mysql query:
SELECT a.id
, ( 3959 * acos( cos( radians(a.latitude) ) * cos( radians( w.latitude ) )
* cos( radians( w.longitude ) - radians(-a.longitude) )
+ sin( radians(a.latitude) ) * sin(radians(w.latitude)) ) ) distance
FROM global_restaurants a
JOIN webgeocities w
ON w.name = a.locality
AND w.country_code = a.country
AND a.latitude LIKE w.latitude
JOIN states s
ON s.state_code = w.state_code
AND w.country_code = s.country_code
WHERE a.city_id = 0
After execution it returns:
id distance
70 6665.478678743614
70 6496.46971480875
70 6725.900646648246
70 6733.5156930808
90 6969.449661399672
90 7252.889875588891
I want it to only return 2 rows with minimum distance like:
id distance
70 6496.46971480875
90 6969.449661399672
add order by at the end of your query:
order by distance ASC LIMIT 2;
now you calculating the distance but
at end of the query you must sort them and limit the returns row count..
.
NOTE:
i think you have a small mistake on your query
the 3959 is for miles but i think the values are in meter
your full query can be like this:
select a.id, ( 6371000 * acos( cos( radians(a.latitude) ) * cos( radians( w.latitude ) )
* cos( radians( w.longitude ) - radians(-a.longitude) ) + sin( radians(a.latitude) ) * sin(radians(w.latitude)) ) ) AS distance
from `global_restaurants` as a INNER JOIN webgeocities as w ON (w.name = a.locality AND w.country_code = a.country and a.latitude like w.latitude) INNER JOIN
states AS s ON (s.state_code = w.state_code and w.country_code = s.country_code) where a.city_id = '0' ORDER BY distance ASC LIMIT 2;
For simplicity, let's assumed that you have your query in view - stack, then your query should look like this
select stack.* from stack join (
SELECT id, GROUP_CONCAT(distance ORDER BY
distance asc) grouped_distance FROM stack GROUP BY id
) group_distance on group_distance.id=stack.id
where find_in_set(stack.distance,group_distance.grouped_distance)between 1 and 2;

How to get minimum radius location

I have 2 tables
1. feeds => id,name
2. feed_locations => id, feed_id, latitude,longitude,location_name
A feed have multiple locations.
now i want to search near by feed from user current location
I require user latitude or longitude so i fetch from google location and radius
Now i use this mysql query
SELECT
`Feed`.`id`,
`Feed`.`name`,
(3959 * acos (cos ( radians(40.7127837) ) * cos( radians( FeedLocation.latitude ) ) * cos( radians( FeedLocation.longitude ) - radians(-74.00594130000002) ) + sin ( radians(40.7127837) ) * sin( radians( FeedLocation.latitude )))) AS `distance`,
(Select COUNT(id) FROM feed_locations WHERE feed_id = `Feed`.`id`) AS `location_count` FROM `feeds` AS `Feed`
LEFT JOIN `feed_locations` AS `FeedLocation` ON (`FeedLocation`.`feed_id` = `Feed`.`id`)
GROUP BY `Feed`.`id` HAVING distance < 10
ORDER BY `distance` ASC
This query giving me result but
i require minimum distance of the feed_locations from user current location. Distance field give me highest value but i require lowest value from feed_locations table.
My query is working but issue it that :
Like if a single feed have 10 location lik 1m,2m,3m,4m,5m,10m,100m distance. and i want to find 5m distance all feed then it works but it shows me that this feed have 5m. distance from me but result should be 1m distance.
I have created a Fiddle please check http://sqlfiddle.com/#!9/c408e6/5
According to the fiddle in table 1 result is coming that feed1 is 4.02m from my current location but this feed have 2 location in table2 i have show each of location distance .so feed1 one of location is zero mile distance from me but it give result that feed1 is 4.02mile
SELECT
a.id,
a.name,
a.distance,
a.location_count
FROM
(SELECT
`Feed`.`id`,
`Feed`.`name`,
(3959 * acos (cos ( radians(40.7127837) ) *
cos( radians( FeedLocation.latitude ) ) *
cos( radians( FeedLocation.longitude ) -
radians(-74.00594130000002) ) + sin ( radians(40.7127837) ) *
sin( radians( FeedLocation.latitude )))) AS `distance`,
(Select COUNT(id)
FROM feed_locations
WHERE feed_id = `Feed`.`id`) AS `location_count`
FROM `feeds` AS `Feed`
LEFT JOIN `feed_locations` AS `FeedLocation` ON (`FeedLocation`.`feed_id` = `Feed`.`id`)
ORDER BY `distance` ASC) AS a
GROUP BY a.id HAVING distance < 10;
In the fiddle I worked out this query you see and it works, now result is 0. The point is that if you group a not ordered set of records and then you order... you do not order anything. You have to order and then group the ordered set.
Hope it works for you.
Regards

Two identical formulas producing differing results

I have a table of items and a table of categories. Each item is saved with it co-ordinates, latitude (lat) and longitude (lon), to allow users to search geographically.
When I do a search for items, those which have exactly the same lat and lon as the user, show in one query but not the other.
One query simply selects all items within a category (2), within a range (<1).
SELECT *, c.name as category, c.category_id as CATid,
( 3959 * acos( cos( radians(52.993252) )
* cos( radians( i.latitude ) )
* cos( radians( i.longitude ) - radians(-0.412470) )
+ sin( radians(52.993252) )
* sin( radians( i.latitude ) ) ) ) AS distance
from items i
join categories c on i.category=c.category_id
where i.category=2 group by i.item_id
HAVING distance < 1
order by distance
The other query selects all the categories and counts the number of items within each category, within the specified geographic range (<1)
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
HAVING distance < 2 ) AS howmanyCat,
( SELECT name FROM categories WHERE category_id = c.parent ) AS parname
FROM categories c ORDER BY category_id, parent
Strangely, if you change the search parameter for distance to 2 on the second query it finds it!
Any ideas?
Here is a fiddle to show what I mean
The second query is assigning the count() value as distance.
The first is assigning the arithmetic calculation as distance.
The first is doing what you want, and it is a clearer query.
EDIT:
I also note that the first query is aggregating by item_id. The second is not doing an explicit aggregation in the outer query, but it is choosing all categories. This is another difference between the versions.

MySQL join multiple tables?

I have 3 tables
tblMarkers
tblReviews
tblImages
I want to return ALL the records from tblMarkers and a count of all the reviews and images for each marker.
To complicate it further it should return all the markers within a radius of a known point.
so the expected result would be
MarkerID-MarkerName-ReviewCount-ImageCount
1223-SomeName-0-1
This is what I have so far and this returns a count of reviews, but I cant get the right SQL to add the count of images
SELECT
`tblMarkers`.`ID`,
`tblMarkers`.`Type`,
`tblMarkers`.`Name`,
`tblMarkers`.`Latitude`,
`tblMarkers`.`Longitude`,
(3959 * acos( cos( radians('45.1') ) * cos( radians( Latitude ) ) * cos( radians( Longitude ) - radians('6') ) + sin( radians('45.1') ) * sin( radians( Latitude ) ) ) )
AS distance,
Count(`tblReviews`.`marker_id`) As reviewCount
FROM
`tblMarkers`
LEFT JOIN `tblReviews` ON `tblMarkers`.`ID` = `tblReviews`.`marker_id`
GROUP BY
`tblMarkers`.`ID`,
`tblMarkers`.`Type`,
`tblMarkers`.`Name`,
`tblMarkers`.`Latitude`,
`tblMarkers`.`Longitude`
HAVING
`distance` < '50'
ORDER BY
distance;
The Images table structure is
ID [primaryKey] (same as tblMarkers.ID)
file
title
How do I add a count of all the images?
Try this:
SELECT M.ID
, M.Type
, M.Name
, M.Latitude
, M.Longitude
, (3959 * acos(cos(radians('45.1')) * cos(radians(M.Latitude )) * cos(radians(M.Longitude) - radians('6')) + sin(radians('45.1')) * sin(radians(M.Latitude )))) AS distance
, IFNULL(COUNT(DISTINCT R.review_id) , 0) AS ReviewCount
, IFNULL(COUNT(DISTINCT I.ID), 0) AS ImageCount
FROM tblMarkers AS M
LEFT JOIN tblReviews AS R ON R.marker_id = M.ID
LEFT JOIN tblImages AS I ON I.marker_id = M.ID
GROUP BY M.ID, M.Type, M.Name, M.Latitude, M.Longitude
HAVING distance < 50
ORDER BY distance
I bet you already know LEFT JOIN. So I used COUNT(DISTINCT R.review_id)) to count all distinct review id's (just make sure that review_id is unique). In case there are no corresponding review and image record for a specific marker record, I used IFNULL(XXX , 0) to display 0.
I used ALIASES to make your query clean and neat. Feel free to ask.
IF ID field of image table is the reference of marker table ID field then the query will be:
SELECT
`tblMarkers`.`ID`,
`tblMarkers`.`Type`,
`tblMarkers`.`Name`,
`tblMarkers`.`Latitude`,
`tblMarkers`.`Longitude`,
(3959 * acos( cos( radians('45.1') ) * cos( radians( Latitude ) ) * cos( radians( Longitude ) - radians('6') ) + sin( radians('45.1') ) * sin( radians( Latitude ) ) ) )
AS distance,
Count(`tblReviews`.`marker_id`) As reviewCount,
Count(`tblImage`.`ID`) As imageCount,
FROM
`tblMarkers`
LEFT JOIN `tblReviews` ON `tblMarkers`.`ID` = `tblReviews`.`marker_id`
LEFT JOIN `tblImage` ON `tblMarkers`.`ID` = `tblImages`.`ID`
GROUP BY
`tblMarkers`.`ID`,
`tblMarkers`.`Type`,
`tblMarkers`.`Name`,
`tblMarkers`.`Latitude`,
`tblMarkers`.`Longitude`
HAVING
`distance` < '50'
ORDER BY
distance;

MySQL distance and min with group by

I have the following query:
SELECT TeacherLocation.user_id, TeacherLocation.city, TeacherLocation.state_id,
TeacherLocation.latitude, TeacherLocation.longitude,
3959 * acos( cos( radians(40.76332092) ) * cos( radians( TeacherLocation.latitude ) )*
cos( radians( TeacherLocation.longitude ) - radians(-73.98623657) ) + sin(
radians(40.76332092) ) *
sin( radians( TeacherLocation.latitude ) ) ) AS distance
FROM teacher_locations AS TeacherLocation
GROUP BY TeacherLocation.user_id
HAVING distance > 0
ORDER BY distance ASC LIMIT 0, 100
This appears to be working fine but the problem I'm having is that since I'm grouping by user_id, and there and that user_id isn't unique in the table, I might and do get the closest distance, but not always the city and state_id of that distance (since there are multiple). Results:
user_id city stateId Lat Long Distance
83224 NEW YORK 33 40.751091 -73.99469757 0.954064642293979
87336 NEW YORK 33 40.751091 -73.99469757 0.954064642293979
87850 NEW YORK 33 40.751091 -73.99469757 0.954064642293979
86822 NEW YORK 33 40.751091 -73.99469757 0.954064642293979
If I add min (distance) I get weird results. Why?
You could use a filtering join, like:
select *
from teacher_locations tl
join (
select user_id
, min(... distance formula here...) as MinDistance
from teacher_location
group by
user_id
) filter
on filter.user_id = tl.user_id
and filter.MinDisance = tl.(... distance formula here...)
If speed is an issue, you can use the MySQL variable trick. This is faster but non-portable (perhaps not even to future MySQL versions.)
select *
from (
select #rn := if(user_id = #lastuserid, 1, #rn+1) as rn
, #lastuserid := user_id
, tl.*
from TeacherLocation tl
cross join
(select #rn := 0, #lastuserid := -1) as init
order by
tl.user_id
, tl.distance desc
) as SubQueryAlias
where rn = 1
Example at SQL Fiddle, you'll have to expand the distance calculation yourself.