SELECT all items in common between two users on three tables - mysql

I have three tables
item_to_user (to store the relations between user and item)
| item_to_user_id | user_id | item_id |
-----------------------------------------
item_tb
| item_id | item_name |
-----------------------
user_tb
| user_id | user_name |
-----------------------
An item can belong to one or more user and viceversa, that's why I'm using the first table.
So, given the user_id = A and user_id = B how can I do a mysql query to select all the items the belong both to user A and user B?
note: I wrote a similar question yesterday but was about two tables not three.

SELECT i.*
FROM item_tb AS i
LEFT JOIN item_to_user AS iu
ON iu.item_id = i.item_id
LEFT JOIN user_tb AS u
ON iu.user_id = u.user_id
WHERE u.user_name IN ('A', 'B')
GROUP BY i.item_id
HAVING COUNT(i.item_id) > 1

By prequerying common items between A and B (via count(*) = 2) will pre-limit the final list of items possible to get details from. Then joining that to the items table as SECOND table in the query should help performance. Especially if A&B have 50 common items, but your items table consists of 1000's of items.
select straight_join
i.item_id,
i.item_name
from
( select iu.item_id
from item_to_user iu
join user_tb u
on iu.user_id = u.user_id
and u.user_name in ( 'A', 'B' )
group by 1
having count(*) = 2 ) Matches,
item_tb i
where
Matches.item_id = i.item_id

If a user can't have repeated items then this simple one will work:
select item_id
from item_to_user
where user_id in ('A', 'B')
group by item_id
having count(*) > 1

Related

Count comments and get average rating from mysql

I just can't figure out how to get average rating and count comments from my mysql database.
I have 3 tables (activity, rating, comments) activity contains the main data the "activities", rating holds the ratings and comments - of course, the ratings.
activity_table
id | title |short_desc | long_desc | address | lat | long |last_updated
rating_table
id | activityid | userid | rating
comment_table
id | activityid | userid | rating
I'm now trying to the data from activity plus the comment_counts and average_rating in one query.
SELECT activity.*, AVG(rating.rating) as average_rating, count(comments.activityid) as total_comments
FROM activity LEFT JOIN
rating
ON activity.aid = rating.activityid LEFT JOIN
comments
ON activity.aid = comments.activityid
GROUP BY activity.aid
...doesn't do the job. It gives me the right average_rating, but the wrong amount of comments.
Any ideas?
Thanks a lot!
You are aggregating along two different dimensions. The Cartesian product generated by the joins affects the aggregation.
So, you should aggregate before the joins:
SELECT a.*, r.average_rating, COALESCE(c.total_comments, 0) as total_comments
FROM activity a LEFT JOIN
(SELECT r.activityid, AVG(r.rating) as average_rating
FROM rating r
GROUP BY r.activityid
) r
ON a.aid = r.activityid LEFT JOIN
(SELECT c.activityid, COUNT(*) as total_comments
FROM comments c
GROUP BY c.activityid
) c
ON a.aid = c.activityid;
Notice that the outer GROUP BY is no longer needed.

Count with Join in mysql

So I have this two table where it records what kind of food is the user's favorite:
users table
------------
id | country
------------
1 | US
2 | PH
3 | US
4 | US
5 | PH
food_favourites table
-----------------
food_id | user_id
-----------------
3 | 1
7 | 1
3 | 2
3 | 3
3 | 4
I want to know how many unique users from US tagged food_id 3 as their favorite.
So far I have this query:
select *, count(user_id) as total
from food_favourite
inner join users on users.id = food_favourites.user_id
where food_favourites.food_id = 3
and users.country = 'US'
group by users.id
Well This doesn't work coz it returns total to 4 instead of just 3.
I also tried doing subqueries - no luck, I think I'm missing something.
DROP TABLE IF EXISTS users;
CREATE TABLE users
(user_id SERIAL PRIMARY KEY
,country CHAR(2) NOT NULL
);
INSERT INTO users VALUES
(1,'US'),
(2,'EU'),
(3,'US'),
(4,'US'),
(5,'EU');
DROP TABLE IF EXISTS favourite_foods;
CREATE TABLE favourite_foods
(food_id INT NOT NULL
,user_id INT NOT NULL
,PRIMARY KEY(food_id,user_id)
);
INSERT INTO favourite_foods VALUES
(3,1),
(7,1),
(3,2),
(3,3),
(3,4);
SELECT COUNT(DISTINCT u.user_id) distinct_users
FROM users u
JOIN favourite_foods f
ON f.user_id = u.user_id
WHERE u.country = 'US'
AND f.food_id = 3;
+----------------+
| distinct_users |
+----------------+
| 3 |
+----------------+
First of all the answer to the above question should be 3 as id 1,3,4 all have food_id 3 as their favorite food.
To just print the query try this, it will surely work:
select count(*) as total from food_favourites
inner join users on users.id=food_favourites.user_id
where food_id=3 and country='US';
I want to know how many unique users from US tagged food_id 3 as their favorite.
You count unique values with COUNT DISTINCT:
select count(distinct ff.user_id) as total
from food_favourite ff
inner join users u on u.id = ff.user_id
where ff.food_id = 3
and u.country = 'US';
Don't group by user, because you don't want a result per user. You want one row with one number, telling you how many US users prefer food 3.
An alternative that I prefer over the join. The query reads like I would word the task: count users from US that like food 3.
select count(*) as total
from users
where country = 'US'
and id in (select user_id from food_favourites where food_id = 3);
No unnecessary join and hence no need to get back to distinct values.
The sub_query is
SELECT u.country, f.food_id, COUNT(u.id) AS 'Total users'
FROM users u
INNER JOIN food_favourites AS f ON (u.id = f.[user_id])
WHERE u.country = 'US'
GROUP BY u.country, f.food_id
select count(user_id) as total, Country
from food_favourites
inner join users on users.id = food_favourites.user_id
where food_favourites.food_id = 3
and users.country = 'US'
group by country
Untested, but I think this is what you're after? This will return results only for the US and a food id of 3. If you want something more reusable that you can simply loop through the results for ALL countries...something like this should work (once again, untested...):
select count(user_id) as total, Country, food_id
from food_favourites
inner join users on users.id = food_favourites.user_id
group by country, food_id
order by country, food_id
Try:
select count(user_id) as total
from food_favourites
inner join users on users.id = food_favourites.user_id
where food_favourites.food_id = 3
and users.country = 'US'

select count items each group

I have two tables category and adverts, i need to select all categories and the number of adverts it has in the adverts table that has at least count greater than zero
the category table
cat_id | Name
----------------
1 | Toys
2 | fashion
3 | electronics
The adverts table
cat_id | title
----------------
1 | a
2 | b
2 | c
1 | d
2 | e
what i expect
cat_id | count | Name
-----------------------
1 |2 | a
2 |3 | b
The query i tried
Select
c.name, c.cat_id,c.parent_id, #count:= (Select Count(av.cat_id) From adsview av Where av.cat_id = c.cat_id)
from
category c WHERE #count > 0
i am getting and empty result, what am i doing wrong?
If you want to make sure that the cat_id from category table are in adverts table you need to join as
select
c.cat_id,
c.Name,
count(a.cat_id) as `count`
from category c
join adverts a on a.cat_id = c.cat_id
group by c.cat_id ;
select cat_id, count(*)
from adverts
group by cat_id;
So the mySQL query engine will grab every single row from the adverts table, it'll put them into neat piles where all rows in the pile have the same category, it'll count the number of rows in each pile, and then it'll return to you a result row for each pile with the pile's id and the number of rows.
Now lets add something: we want to also get the category's name. So we indicate that in the select clause, and add a join to the from clause. The join says "for every row in table a, consider it alongside every row in table b. if some condition holds, put this combined row into the from set". You can see that joins are actually quite slow in SQL (relatively).
select c.cat_id, count(*) as count, c.name
from adverts as a join categories as c on a.cat_id = c.cat_id
group by c.cat_id;
Note also that I've aliased the tables as a and c respectively, so as to remove the ambiguity over the column name cat_id (otherwise the mySQL query engine may get confused).
You can try this, mate:
SELECT
c.cat_id,
COUNT(a.cat_id) AS count,
a.title
FROM
category c
LEFT JOIN adverts a ON a.cat_id = c.cat_id
GROUP BY
c.cat_id
HAVING
count > 0;
or this:
SELECT
c.cat_id,
COUNT(a.cat_id) AS count,
a.title
FROM
category c
INNER JOIN adverts a ON a.cat_id = c.cat_id
GROUP BY
c.cat_id;
You have to use group by function like below
select cat_id, count(*) as count
from adverts
group by cat_id;

Joining MySQL tables in a tricky way

I have three tables
table persons
id | name
table files
id | person_data | person_id
table items
id | item_name | file_id
Each person has multiple files and each file has multiple items.
Is there any way to select for each person all it's items using just mysql returning something like this:
person - file_id -> item 1, file_id -> item 2, file_id -> item 3
I was thinking on some type of JOIN, i tried all combinations that i could imagine but i think that join it's not a solution, or...
Use GROUP_CONCAT.Please check SQLFiddle
SELECT
b.id,b.name,
GROUP_CONCAT(b.items SEPARATOR '|')
FROM (SELECT
p.id,p.name,
CONCAT(f.id,' -> ', GROUP_CONCAT(i.item_name)) AS items
FROM persons p,
files f,
items i
WHERE p.id = f.person_id
AND f.id = i.file_id
GROUP BY f.id) AS b
GROUP BY b.id
SELECT ti.id, ti.item_name
FROM TableItems ti
INNER JOIN TableFiles tf ON tf.id = ti.file_id
INNER JOIN TablePersons tp ON tp.id = tf.person_id
WHERE tp.id = :id
Where :id is the id of the person you want to list items for.

mysql multi count() in one query

I'm trying to count several joined tables but without any luck, what I get is the same numbers for every column (tUsers,tLists,tItems). My query is:
select COUNT(users.*) as tUsers,
COUNT(lists.*) as tLists,
COUNT(items.*) as tItems,
companyName
from users as c
join lists as l
on c.userID = l.userID
join items as i
on c.userID = i.userID
group by companyID
The result I want to get is
---------------------------------------------
# | CompanyName | tUsers | tlists | tItems
1 | RealCoName | 5 | 2 | 15
---------------------------------------------
what modifications do i have to do to my query to get those results?
Cheers
Try this
SELECT u.userID, companyName,
Count(DISTINCT l.listid) as tLists, Count(DISTINCT i.items) as tItems
FROM users u
LEFT JOIN lists l ON u.userID=l.userID
LEFT JOIN items i ON u.userID=i.userID
GROUP BY u.companyID
You can do it by using sub query
select (select count(*) from users where userID=YourUserID) tUsers,
(select count(*) from lists where userID=YourUserID) as tLists,
(select count(*) from items where userID=YourUserID) as tItems,
companyName
from company group by companyID