Distinct not working properly - mysql

I've wrote a query with DISTINCT but I still get duplicate records returned.
SELECT DISTINCT
`cuisine_types`.`id`,
`cuisine_types`.`name`,
`cuisine_types`.`image`,
(
SELECT group_concat(`tagname` separator ', ')
FROM `cuisine_tags`
WHERE `cuisine_type` = `cuisine_types`.`id`
) AS tags,
(
3959 * acos( cos( radians(" . $dLat . ") ) * cos( radians( gps_lat ) ) * cos( radians( gps_lon ) - radians(" . $dLon . ") ) + sin( radians(" . $dLat . ") ) * sin( radians( gps_lat ) ) )
) AS distance
FROM `company`
LEFT JOIN `cuisine_types`
ON
`company`.`cuisine_type_id` = `cuisine_types`.`id`
HAVING
distance < " .$dMiles
When I try using the GROUP BY function my query isn't working properly.
When I use GROUP BY I place it above my HAVING:
GROUP BY `cuisine_types`.`name`
Examples:
When I use it with these values:
$dLat = '52.779716';
$dLon = '21.84803';
$iKm = '30';
It returns:
id = 1
name = Snackbar
image =
tags = Patat, Snacks
distance = 17.4713944772963
When I use $iKm with 3000 it returns this row as well:
id = 1
name = Snackbar
image =
tags = Patat, Snacks
distance = 722.407714147792
So I get two records.
When I use this with groupby and $iKm = 30; It returns nothing. With a value of 3000 it returns one row. But I have one record with a distance of 17 miles so thats below 30.

This should fix your problem, the issue was the distance calculation blocked the GROUP BY function. By putting the equation in the HAVING itself, the problem seemed to be fixed. Sorry I can't explain it in more detail.
SELECT
`cuisine_types`.`name`,
`cuisine_types`.`id`,
`cuisine_types`.`image`
`c`.`gps_lat` as lat,
`c`.`gps_lon` as lon,
(SELECT group_concat(`tagname` separator ', ') FROM `cuisine_tags` WHERE `cuisine_type`=`cuisine_types`.`id`) as tags FROM `company` as c LEFT JOIN `cuisine_types` ON c.`cuisine_type_id`
= `cuisine_types`.`id` GROUP BY `cuisine_types`.`name` HAVING ( 3959 * acos( cos( radians(52.779716) ) * cos( radians( lat ) ) * cos( radians( lon ) - radians(21.84803) ) + sin( radians(52.779716) ) * sin( radians( lat ) ) ) ) < 2000;

Related

How to write SQL JOIN query with geolocation in php

I am new to sql.
Sample of offerings Table
offering_id | offering_meal_id | offering_name | offering_profile_id | Offering_Expiration_Date_Time | Lat | Lon
Sample of profile Table
profile_id | fullname
I am passing following parameters $latitude , $longitude, $meal_id
I want to write a query which will
get all data from offerings table in basis of latitude and longitude and Offering_Expiration_Date_Time is greater than or equal to today's date. For this I have following calculation which is working.
SELECT *, ( 6371 * ACOS( COS( RADIANS( ".$latitude." ) ) *
COS( RADIANS( Lat ) ) * COS( RADIANS( Lon ) - RADIANS( ".$longitude." ) ) + SIN( RADIANS( ".$latitude." ) )
* SIN( RADIANS( Lat ) ) ) ) AS distance FROM offerings HAVING distance < 3 AND Offering_Expiration_Date_Time>='".date('Y-m-d h:i:s')."'
Next Select only those offering which meal_id = $meal_id
Join profile table and get fullname from it where offerings.offering_profile_id = profile.profile_id
order it by offering_id
EDIT
SELECT offerings.*,profile.fullname,
( 6371 * ACOS( COS( RADIANS( 16.691120 ) ) * COS( RADIANS( Lat ) ) * COS( RADIANS( Lon ) - RADIANS( 74.219978 ) ) + SIN( RADIANS( 16.691120 ) ) * SIN( RADIANS( Lat ) ) ) )
AS distance
FROM offerings
INNER JOIN profile
ON offerings.offering_profille_id = profile.profile_id
WHERE distance < 3
AND offerings.offering_meal_id = 2
AND Offering_Expiration_Date_Time>='2017-08-23 15:43:00'
ORDER BY offerings.offering_id
LIMIT 10
Before your having clause, join to the profile table like so:
Inner join profile on offerings.offering_profile_id = profile.profile_id
Then include at end:
Order by offerings.offering_id
Also make sure to include in your select statement
Profile.fullname
Also change having to where and add this into your where condition:
And offerings.offering_meal_id = $meal_id

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

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.

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;