Count with Join in mysql - 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'

Related

How to join the same table more than once to select different columns?

I need to find out users who have either made or received a booking.
I have two tables that look like this:
Users:
+----+
| id |
+----+
| 1 |
| 2 |
| 3 |
+----+
Bookings:
+----+-----+-----+
| id | rid | oid |
+----+-----+-----+
| 1 | 1 | 2 |
| 2 | 2 | 1 |
| 3 | 3 | 4 |
+----+-----+-----+
A booking has two users, a 'rider' (rid), and an 'owner' (oid).
The rider and owner can't be the same for each booking but riders can also be owners.
My output should be a list of user IDs that correspond with users who have made or received a booking.
So far I have written
select u.id, b1.rid, b2.oid
from users u
left join bookings b1
on u.id = b1.rid
left join bookings b2
on u.id = b2.oid;
And various other permutations, but I'm not getting the desired result. Any help would be appreciated.
You want all User IDs that are either in Bookings.rid or Bookdings.oid. So you could do something like:
select
users.id
from
users
where
users.id in (select bookings.rid from bookings)
or
users.id in (select bookings.oid from bookings);
You should be able to utilize a UNION clause here.
However, you don't define what the "time window" is, so I am not sure we can come up with a complete solution for you. However, try something like the following:
SELECT
users.id,
bookings.rid,
bookings.oid
FROM
users
LEFT JOIN bookings ON users.id = bookings.rid
UNION ALL
SELECT
users.id,
bookings.rid,
bookings.oid
FROM
users
LEFT JOIN bookings ON users.id = bookings.oid
My output should be a list of user IDs that correspond with users who have made or received a booking.
To do that, you only need to look at the bookings table :
SELECT DISTINCT rid id FROM bookings
UNION ALL SELECT DISTINCT oid FROM bookings
The DISTINCT removes the duplicates returned by each query, and the UNION ALL removes duplicates across both queries.
If you are looking to filter by time frame :
SELECT DISTINCT rid id FROM bookings WHERE some_date BETWEEN :start_date AND :end_date
UNION ALL SELECT DISTINCT oid FROM bookings WHERE some_date BETWEEN :start_date AND :end_date
Where some_date is the field that contains the booking date, and :start_date/end_date are the beginning and the end of the date interval.
I guess there is a name column in Users table.
If you want this too then:
select users.id, users.name from (
select rid userid from bookings
union
select oid userid from bookings
) t inner join users
on users.id = t.userid
group by users.id, users.name
See the demo
If not you only need to scan the bookings table:
select distinct userid from (
select rid userid from bookings
union
select oid userid from bookings
) t
See the demo

How to get different results from one mysql table?

I have a user table in the database where all users of the system are stored.
The table has a user_id and a business_name and a first_name.
Some users are merchants and get a business name,
some users are consumers and get a first name.
In a second table I have transactions with a user_id and a merchant_id (which are defining the transaction) and an amount. Both ids reference to user table.
Table users:
user_id bus_name first_name role_id
1 Thomas 10
2 comp1 7
3 Peter 10
4 comp2 7
(role_id is defining with 10=consumer, 7=merchant)
Table transactions:
trans_id amount user_id merchant_id
1 12 1 2
2 23 3 2
3 34 3 4
4 19 1 4
Now I want to have a query with a result as one table:
This table should contain the transaction with amount, user_id, first_name, merchant_id and bus_name.
I want to get this result:
trans_id amount user_id first_name merchant_id bus_name
1 12 1 Thomas 2 comp1
2 23 3 Peter 2 comp1
3 34 3 Peter 4 comp2
4 19 1 Thomas 4 comp2
I have the problem that either I get only the first_name and empty bus_name or I get only the bus_name but empty first_name.
I am using a left join:
...
left join `users`
on(
(`transactions`.`user_id` = `users`.`user_id`)
)
...
But for this I would get for user_id=1 the first_name=Thomas and the bus_name='' would be empty because I only reference to one line in table and not also to different user with user_id=2.
But I want to say something like:
for trans_id=1
get first_name FROM users WHERE transactions.user_id = users.user_id
AND
get bus_name FROM users WHERE transactions.merchant_id = users.user_id
Thanks for your help, I tried so many things but it does not work.
You have to join the user table twice:
SELECT t.*, u.first_name, m.bus_name
FROM transactions t
JOIN users as u
ON t.user_id = u.user_id
JOIN users as m
ON t.merchant_id = m.merchant_id
you could use a duoble join in users table
select a.trans_id, a.amount , a.user_id, b.first_name, a.merchant_id, c. bus_name
from transactions a
inner join users b on a.user_id = b.user_id and b.role_id = 10
inner join users c on a.merchant_id = c.user_id and c.role_id = 7
To join the user table twice worked fine. With "left join users as consumer" I create a kind of a virtual users table called "consumer", this one is joined. Of course in select I had to adjust table name as well. Same for second "virtual" table od users, called "merchant".
select
`transactions`.`trans_id` AS `trans_id`,
`transactions`.`merchant_id` AS `merchant_id`,
`merchant`.`bus_name` AS `bus_name`,
`transactions`.`user_id` AS `user_id`,
`consumer`.`first_name` AS `first_name`,
`cards`.`card_id` AS `card_id`,
`cards`.`serial_no` AS `serial_no`
from (
`transactions`
left join `cards`
on(
(`cards`.`card_id` = `transactions`.`card_id`)
)
left join `users` as consumer
on(
(`consumer`.`user_id` = `transactions`.`user_id`)
)
left join `users` as merchant
on(
(`merchant`.`user_id` = `transactions`.`merchant_id`)
)
)

SQL COUNT Return Wrong Number

so I have a problem with my query. I have 2 tables:
courses:
The user_id in this table is the instructor of the course.
-----------------------------------------------------------------------
| course_id | user_id | course_name | other information |
-----------------------------------------------------------------------
| 6 | 1 | My Course 1 | ... |
-----------------------------------------------------------------------
my_courses:
The user_id in this table is a student of the course.
--------------------------------------------------
| user_id | course_id | created_at |
--------------------------------------------------
| 5 | 6 | [UNIX_TIMESTAMP] |
--------------------------------------------------
The my_courses contains the number of people that have joined that course. I want to get all the course info as well as the number of people that have joined a course. Everything returns as expected except the number of people that joined a course. This is the query I'm using:
SELECT
courses.*,
users.name, //This is the name of the instructor
users.last_name, //This is the last name of the instructor
COUNT(my_courses.user_id) as count_students
FROM courses
LEFT JOIN users
ON courses.user_id = courses.user_id
LEFT JOIN my_courses
ON courses.course_id = my_courses.course_id
WHERE courses.course_id = '6'
Like I said, this query returns the course info like normal but returns 3 as count_students when it should only return 1. Does anyone know why this is happening? Any help is greatly appreciated.
It's probably because of the JOIN condition for users. It should be:
LEFT JOIN users
ON courses.user_id = users.user_id
You should also add a GROUP BY clause in your query:
SELECT
c.*,
u.name,
u.last_name,
COUNT(mc.user_id) AS count_students
FROM courses c
LEFT JOIN users u
ON c.user_id = u.user_id
LEFT JOIN my_courses mc
ON c.course_id = mc.course_id
WHERE c.course_id = '6'
GROUP BY
<columns not in the aggregate function>
Additionally, alias your tables to improve readability.
JOIN operations cause combinatorial multiplication of rows. You need to summarize the student count from its own table like so.
SELECT course_id, COUNT(*) students
FROM my_courses
GROUP BY course_id
That gives you a result set with either one or zero rows per course_id. You can then join it to the rest of your query.
SELECT courses.*,
users.name, //This is the name of the instructor
users.last_name, //This is the last name of the instructor
aggr.count_students
FROM courses
LEFT JOIN users ON courses.user_id = courses.user_id
LEFT JOIN (
SELECT course_id, COUNT(*) students
FROM my_courses
GROUP BY course_id
) aggr ON courses.course_id = aggr.course_id
WHERE courses.course_id = '6'
That way you'll avoid multiple-counting your students for courses with, perhaps, more than one instructor.

SELECT all items in common between two users on three tables

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

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