MySQL count when value is not equal - mysql

I have few tables.
1.region
2.restaurant
3.restaurant_itmes
region
id | name
................
1 | NY
2 | Paris
3 | London
restaurant
id | name | region_id
.........................
1 | KFC | 1
2 | McDonals'| 1
3 | La res | 2
4 | Queen's | 3
restaurant_items
id | name | restaurant_id | pro_pic | featured_pic
...................................................
1 |Pizza |3 | null | defaut.jpg
2 |Pizza |4 | pizza.jpg | defaut.jpg
3 |Burger|1 | burger.jpg| burger.jpg
4 |Burger|2 | burger.jpg| burger.jpg
5 |Burger|3 | null | burger.jpg
6 |Burger|4 | null | default.jpg
7 |Donat |2 | null | default.jpg
8 |Fries |2 | null | default.jpg
I want to generate an query to populate this table
region |Number of restaurants |total items | items_with_pro_pic | items_with_featured_pic
............................................................................................
NY | 2 | 4 | 2 | 2
Paris | 1 | 2 | 0 | 1
London | 1 | 2 | 1 | 0
What I have done upto now is
SELECT region.name, count(restaurant_items.id) as total_items, count(restaurant_items.pro_pic)
INNER JOIN restaurant on restaurant_items.restaurant_id = restaurant.id
INNER JOIN region on restaurant.region_id = region.id
GROUP BY region.name;
Here I can get items_with_pro_pic by count(restaurant_items.pro_pic)
but I can't do that for items_with_featured_pic because featured_pic is not null able if there is no value
default value is default.jpg.
So I tried count(restaurant_items.featured_pic != 'defaut.jpg') but this doesn't work for me.
And how could I get number of restaurants since it is not a part of restaurant_items table?
How do I achieve these two using MySQL?

You can change the COUNT to a SUM and run it over an IF statement:
SUM(IF(restaurant_items.featured_pic != 'default.jpg',1,0))
Or alternatively you can specify it as a COUNT if you want, but the ELSE portion will need to be a NULL rather than a 0 since otherwise it will still count it:
COUNT(IF(restaurant_items.featured_pic != 'default.jpg',1,NULL))
To count number of restaurants, you can simply do a distinct count:
COUNT(DISTINCT restaurant.id)
A few small extra tips:
You may want to change the name of 'restaurant_items' to 'restaurant_item' since that suits the naming convention of the other tables
You should be aliasing your table names in the FROM and JOIN clauses, since it enhances code legibility

You can use a case expression to produce nulls instead of default.jpg in order to use in a count function:
SELECT region.name,
COUNT(restaurant_items.id) as total_items,
COUNT(restaurant_items.pro_pic),
COUNT(CASE WHEN restaurant_items.featured_pic != 'default.jpg' THEN 1 END)
INNER JOIN restaurant ON restaurant_items.restaurant_id = restaurant.id
INNER JOIN region ON restaurant.region_id = region.id
GROUP BY region.name;

use case when
SELECT region.name, count(restaurant_items.id) as total_items, count(restaurant_items.pro_pic),
count(case when restaurant_items.featured_pic != 'defaut.jpg' then 1 end) as
items_with_featured_pic,
count(case when pro_pic is null then 1 end) as
items_with_pro_pic_null
INNER JOIN restaurant on restaurant_items.restaurant_id = restaurant.id
INNER JOIN region on restaurant.region_id = region.id
GROUP BY region.name

Related

How to join has many relation table and fetch result by type

I have a few tables which I am trying to join and fetch the results for a list
Interviews Table
+--------------+-----------+
| interview_id | Candidate |
+--------------+-----------+
| 1 | Ram |
| 2 | Rahim |
| 3 | Joseph |
+--------------+-----------+
Participant Ratings Table
+--------------+-----------+-------+
| interview_id | Rater Type|Rating |
+--------------+-----------+-------+
| 1 | Candidate | 4 |
| 2 | Candidate | 4 |
| 1 | Recruiter | 5 |
+--------------+-----------+-------+
System Ratings Table
+--------------+------------+-------+
| interview_id | Rating Type|Rating |
+--------------+------------+-------+
| 1 | Quality | 4 |
| 1 | Depth | 4 |
| 1 | Accuracy | 5 |
| 2 | Quality | 4 |
| 2 | Depth | 3 |
| 2 | Accuracy | 5 |
| 3 | Quality | 4 |
| 3 | Depth | 5 |
| 3 | Accuracy | 5 |
+--------------+------------+-------+
I need to fetch the result of average ratings for each interview given in the following manner.
+--------------+--------------+-----------------+-----------------+
| interview_id | System Rating|Recruiter Rating |Candidate Rating |
+--------------+--------------+-----------------+-----------------+
| 1 | 4.3 | 5 | 4 |
| 2 | 4.0 | 0 | 4 |
| 3 | 4.6 | 0 | 0 |
+--------------+--------------+-----------------+-----------------+
Each interview can will have one 1 candidate rating and 1 recruiter rating but that is optional. If given a record is created in participant rating with rating and type.
Need to get the average of system ratings of all the types and get one value as system rating and if rating provided by participants then display else display as 0 if any or both the participants not provided any rating.
Please ignore the values, if there is a mistake.
The SQL which I tried to get the result.
SELECT i.candidate, i.id AS interview_id,
AVG(sr.rating) AS system_rating,
AVG(CASE WHEN pr.rater_type = 'Candidate' THEN pr.rating END) AS candidate_rating,
AVG(CASE WHEN pr.rater_type = 'Recruiter' THEN pr.rating END) AS recruiter_rating
FROM system_ratings sr, participant_ratings pr, interviews i
WHERE sr.interview_id = i.id AND i.id = 2497 AND pr.interview_id = i.interview_id
The problem is whenever participant ratings are not present then results are missing as there is join.
Use LEFT JOIN to make sure if relation tables do not have any data, still we can have records from the main table.
Reference: Understanding MySQL LEFT JOIN
Issue(s):
Wrong field name: pr.interview_id = i.interview_id, it should be pr.interview_id = i.id as we don't have any interview_id field in interviews table, it would be id field - based on your query.
pr.interview_id = i.id in where clause: If participant_rating table does not have any records for a given interview, this will cause the removal of that interview from the result set. Use LEFT JOIN for participant_rating table.
sr.interview_id = i.id in where clause: If system_rating table does not have any records for a given interview, this will cause the removal of that interview from the result set. Use LEFT JOIN for system_rating table too.
Usage of AVG works but won't work for other aggregates functions like SUM, COUNT.. because if we have one to many relationships then join will make there will be multiple records for the same row.
Solution:
SELECT
i.id AS interview_id,
i.candidate,
AVG(sr.rating) AS system_rating,
AVG(CASE WHEN pr.rater_type = 'Candidate' THEN pr.rating END) AS candidate_rating,
AVG(CASE WHEN pr.rater_type = 'Recruiter' THEN pr.rating END) AS recruiter_rating
FROM interviews i
LEFT JOIN system_rating sr ON sr.interview_id = i.id
LEFT JOIN participant_rating pr ON pr.interview_id = i.id
-- WHERE i.id IN (1, 2, 3) -- use whenever required
GROUP BY i.id

Querying MySQL tables for a item a user hasnt 'scored' yet

Tables
__________________ ________________________________
|______name________| |____________scores______________|
|___id___|__name___| |_id_|_user-id_|_name-id_|_score_|
| 1 | bob | | 1 | 3 | 1 | 5 |
| 2 | susan | | 2 | 1 | 3 | 4 |
| 3 | geoff | | 3 | 3 | 2 | 3 |
| 4 | larry | | 4 | 2 | 4 | 5 |
| 5 | peter | | 5 | 1 | 1 | 0 |
-------------------- ----------------------------------
Im looking to write a query that returns a RANDOM name from the 'name' table, that the user hasnt scored so far.
So given user '1' for example, it could return 'susan, larry or peter' as user '1' hasnt given them a score yet.
SELECT *
FROM names
LEFT JOIN
votes
ON names.id = votes.name_id
WHERE votes.user_id = 1
AND (votes.score IS NULL);
So far I have this, but it doesnt seem to be working as I would like
(atm it doesnt return a random, but all, but this is wrong)
Any help would be appreciated.
If you are filtering on some field of outer joined table type of join is automatically changed to inner. In your case it's condition
votes.user_id = 1
So you need to move that condition from WHERE to ON
SELECT *
FROM names
LEFT JOIN
votes
ON names.id = votes.name_id and votes.user_id = 1
WHERE (votes.score IS NULL);
Consider moving the condition from WHERE to JOIN ON clause since you are performing an OUTER JOIN else the effect would be same as INNER JOIN
LEFT JOIN votes
ON names.id = votes.name_id
AND votes.user_id = 1
WHERE votes.score IS NULL
ORDER BY RAND();
You could apply :
SELECT name FROM name join scores on name.id=scores.user_id WHERE scores.score=0
You can perform this as a sub-query
SELECT *
FROM names
WHERE id NOT IN (SELECT name_id FROM votes WHERE user_id=1)
ORDER BY RAND()
LIMIT 1

how to JOIN table with WHERE clause and also show non existent rows

The title may sound a bit confusing, but let me explain:
I have three tables:
store
storeID | name
1 | store1
2 | store2
3 | store3
product
productID | name
1 | ball
2 | cup
store_product
storeID_fk | productID_fk
2 | 1
1 | 2
3 | 2
What I want to archive is a result like the following:
storeID | name | storeID_fk | productID_fk
1 | store1| NULL | NULL
2 | store2| 2 | 1
3 | store3| NULL | NULL
What I have tried so far:
SELECT * FROM `store`
LEFT JOIN `store_product` on storeID = storeID_fk
WHERE productID_fk = 1;
But this only returns:
storeID | name | storeID_fk | productID_fk
2 | store2| 2 | 1
How can I also display the empty/not existing rows?
When doing a left join, put the right side table's conditions in the ON clause to get true left join behavior. (When in WHERE, you get regular inner join result.)
SELECT * FROM `store`
LEFT JOIN `store_product` on storeID = storeID_fk
AND productID_fk = 1

MySQL select unique rows in two columns with the highest value in one column

I have a basic table:
+-----+--------+------+------+
| id, | name, | cat, | time |
+-----+--------+------+------+
| 1 | jamie | 1 | 100 |
| 2 | jamie | 2 | 100 |
| 3 | jamie | 1 | 50 |
| 4 | jamie | 2 | 150 |
| 5 | bob | 1 | 100 |
| 6 | tim | 1 | 300 |
| 7 | alice | 4 | 100 |
+-----+--------+------+------+
I tried using the "Left Joining with self, tweaking join conditions and filters" part of this answer: SQL Select only rows with Max Value on a Column but some reason when there are records with a value of 0 it breaks, and it also doesn't return every unique answer for some reason.
When doing the query on this table I'd like to receive the following values:
+-----+--------+------+------+
| id, | name, | cat, | time |
+-----+--------+------+------+
| 1 | jamie | 1 | 100 |
| 4 | jamie | 2 | 150 |
| 5 | bob | 1 | 100 |
| 6 | tim | 1 | 300 |
| 7 | alice | 4 | 100 |
+-----+--------+------+------+
Because they are unique on name and cat and have the highest time value.
The query I adapted from the answer above is:
SELECT a.name, a.cat, a.id, a.time
FROM data A
INNER JOIN (
SELECT name, cat, id, MAX(time) as time
FROM data
WHERE extra_column = 1
GROUP BY name, cat
) b ON a.id = b.id AND a.time = b.time
The issue here is that ID is unique per row you can't get the unique value when getting the max; you have to join on the grouped values instead.
SELECT a.name, a.cat, a.id, a.time
FROM data A
INNER JOIN (
SELECT name, cat, MAX(time) as time
FROM data
WHERE extra_column = 1
GROUP BY name, cat
) b ON A.Cat = B.cat and A.Name = B.Name AND a.time = b.time
Think about it... So what ID is mySQL returning form the Inline view? It could be 1 or 3 and 2 or 4 for jamie. Hows does the engine know to pick the one with the max ID? it is "free to choose any value from each group, so unless they are the same, the values chosen are indeterminate. " it could pick the wrong one resulting in incorrect results. So you can't use it to join on.
https://dev.mysql.com/doc/refman/5.0/en/group-by-handling.html
If you want to use a self join, you could use this query:
SELECT
d1.*
FROM
date d1 LEFT JOIN date d2
ON d1.name=d2.name
AND d1.cat=d2.cat
AND d1.time<d2.time
WHERE
d2.time IS NULL
It is very simple
SELECT MAX(TIME),name,cat FROM table name group by cat

Joining tables but needs 0 for empty rows

I don't know how to explain the scenario using words. So am writing the examples:
I have a table named tblType:
type_id | type_name
---------------------
1 | abb
2 | cda
3 | edg
4 | hij
5 | klm
And I have another table named tblRequest:
req_id | type_id | user_id | duration
-------------------------------------------
1 | 4 | 1002 | 20
2 | 1 | 1002 | 60
3 | 5 | 1008 | 60
....
So what am trying to do is, fetch the SUM() of duration for each type, for a particular user.
This is what I tried:
SELECT
SUM(r.`duration`) AS `duration`,
t.`type_id`,
t.`type_name`
FROM `tblRequest` AS r
LEFT JOIN `tblType` AS t ON r.`type_id` = t.`type_id`
WHERE r.`user_id` = '1002'
GROUP BY r.`type_id`
It might return something like this:
type_id | type_name | duration
-------------------------------
1 | abb | 60
4 | hij | 20
It works. But the issue is, I want to get 0 as value for other types that doesn't have a row in tblRequest. I mean I want the output to be like this:
type_id | type_name | duration
-------------------------------
1 | abb | 60
2 | cda | 0
3 | edg | 0
4 | hij | 20
5 | klm | 0
I mean it should get the rows of all types, but 0 as value for those type that doesn't have a row in tblRequest
You could perform the aggregation on tblRequest and only then join it, using a left join to handle missing rows and coalesce to convert the nulls to 0s:
SELECT t.type_id, type_name, COALESCE(sum_duration, 0) AS duration
FROM tblType t
LEFT JOIN (SELECT type_id, SUM(duration) AS sum_duration
FROM tblRequest
WHERE user_id = '1002'
GROUP BY type_id) r ON t.type_id = r.type_id
Select a.type_id, isnull(sum(b.duration), 0)
From tblType a Left Outer Join tblRequest b
ON a.type_id = b.type_id and b.user_id = 1002
Group by a.type_id