MySQL check if row with category exists - mysql

I'm using the Haversine formula with this query and it works until. The goal is to check if each row has certain categories in an value of $deflin which looks like $deflin = category1, category2, category3. The results will show rows within 50km and if it contains any of the categories defined by $deflin. Not sure how to approach this either with WHERE IN or LIKE. Any help is appreciated.
MySQL for query
$awaka = "SELECT *,
( 6371 * acos( cos( radians(?) ) * cos( radians(job_latitude) ) *
cos(radians(?) - radians(job_longitude) ) + sin( radians(?) ) *
sin( radians(job_latitude) ) ) ) AS distance FROM job, users
WHERE job.listee_id = users.user_id AND job.job_category LIKE ?
HAVING distance < 50";
$result = $this->db->query($awaka, array($conlat, $conlong, $conlat, $deflin));

I don't think you like to use the LIKE operator since it only searches for patterns within a column and you only wan't to return rows that really has one of your categories. Instead you should use an IN clause to check if a job has one of your categories:
// contains the id for each category
$categories = array(1, 4, 5, 6);
$deflin = implode(',', $categories);
// if job_category is a text column you could do like this instead
$categories = array('category1', 'category2', 'category3');
$deflin = implode(',', $categories);
$awaka = "SELECT *,
( 6371 * acos( cos( radians(?) ) * cos( radians(job_latitude) ) *
cos(radians(?) - radians(job_longitude) ) + sin( radians(?) ) *
sin( radians(job_latitude) ) ) ) AS distance FROM job, users
WHERE job.listee_id = users.user_id AND job.job_category IN ($deflin)
HAVING distance < 50";

Related

using first sql statement result into another sql statement

What basically i want to do is pick all the coordinates from roadData
one by one and then find all the point in tweetMelbourne within 20
miles of it and insert those point into another table.
So for every (x,y) in roadData table find neighbouring data point from
tweetMelbourne and insert those points into another new table.
So I have to do this:
SELECT geo_coordinates_latitude, geo_coordinates_longitude
FROM tweetmelbourne
HAVING ( 3959 * acos( cos( radians(latitude) ) * cos( radians( geo_coordinates_latitude ) ) *
cos( radians( geo_coordinates_longitude ) - radians(longitude) ) + sin( radians(latitude) ) *
sin( radians( geo_coordinates_latitude ) ) ) ) < .1 ORDER BY distance LIMIT 0 , 20;
in which the value of latitude and longitude i have to get from another table :
select longitude,latitude from roadData;
describe tweetmelbourne;
describe roadData;
SELECT geo_coordinates_latitude, geo_coordinates_longitude
FROM tweetmelbourne;
select longitude,latitude from roadData;
The correct syntax of IN() with multiple arguments is : (Val1,Val2) IN(SELECT VAL1,val2..
SELECT t.address,(t.x+t.y) as z
FROM student t
WHERE (t.x,t.y) IN(SELECT x,y FROM tweet)
Also can be done with a join :
SELECT t.address,(t.x+t.y) as z
FROM student t
JOIN tweet s
ON(t.x = s.x and t.y = s.y)
EDIT: I think what you want is:
SELECT s.address,t.x+t.y as z
FROM student s
CROSS JOIN tweet t
Try this:
SELECT s.address, (t.x + t.y) as z
from (SELECT id,x,y FROM `tweet`) as t, student s
WHERE t.id = s.id;
You need to join the two tables, calculating the distance in the ON clause to select the nearby rows.
SELECT *
FROM tweetmelbourne
JOIN roadData
ON ( 3959 * acos( cos( radians(latitude) ) * cos( radians( geo_coordinates_latitude ) ) *
cos( radians( geo_coordinates_longitude ) - radians(longitude) ) + sin( radians(latitude) ) *
sin( radians( geo_coordinates_latitude ) ) ) ) < .1
This will be very slow if the tables are large. It's not possible to use indexes to implement the join, so it will have to perform that complex formula on every pair of rows. You might want to look at MySQL's Spatial Data extensions.

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 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;

Distinct not working properly

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;