GROUP BY on multiple joins - mysql

I have searched for the issue but could not have a working solution.
I have 3 tables: User, Post, Comments.
select u.id user
, p.id post
, c.id comm
from USer u
join Post p
on u.id = p.user_id
join Comments c
on p.id = c.post_id;
This gives me an output with 3 columns that relates a user with the posts they have and the comments received on each.
The o/p is like:
user post comm
1 1 4
1 1 5
1 1 7
1 1 8
2 5 11
2 5 12
2 7 13
I wanted to find the user with the maximum number of overall comm. For this the GROUP BY on user is not working. What could be an alternative for the task?

You can use GROUP BY, ORDER BY, and LIMIT:
select p.user_id, count(*) as num_comments
from Post p inner join
Comments c
on p.id = c.post_id
group by p.user_id
order by num_comments desc
limit 1;
Note that because you only want the user id, you do not need the user table, because user_id is in post.

Related

Getting recent user likes in MySQL

I have a site where user like products, i need for each product to show the list of up to 10 users who recently liked the product ordered by created at desc and their avatars. I am trying to find an efficient way to do that, note that a product can have 1000's of likes, and to be efficient I only want to show the last 10 likes.
I have two tables
products
id, title, ....
likes
id, user_id, product_id, created_at
I would like to get up to 10 user ids who liked the product recently. from there I will do another query on the user ids to get their names and avatars, but how do I make this first query to get the user ids for each product ?
so result should be
product_id, liked by
1 12,23,45,67
2 13,4,5
3 1
etc
Have you tried running this query:
SELECT p.id AS product_id, u.username, u.avatar, u.id AS user_id FROM products AS p
LEFT JOIN likes AS l ON l.produc_id = p.product_id
LEFT JOIN users AS u ON u.id = l.user_id
WHERE p.id = {PRODUCT_ID} -- If you want it for a single product
ORDER BY l.created_at DESC
LIMIT 10
This will return you the list of 10 usernames and their avatars for a product all in one query.
You might need to adjust this query as you have not provided detailed explanation of your table and their relations.
Edit:
adding another sql example as per request:
SELECT l.product_id, p.name AS product_name, GROUP_CONCAT(l.user_id) AS listOfUsers, GROUP_CONCAT(u.username) AS username, GROUP_CONCAT(u.avatar) AS avatars FROM likes AS l
LEFT JOIN products AS p ON p.id = l.product_id
LEFT JOIN users AS u ON u.id = l.user_id
WHERE l.product_id IN(1,2,3)
ORDER BY l.created_at DESC
LIMIT 10
This should return something like
product_id | listOfUsers | usernames | avatars
1 | 1,2,3 | test1,test2,test3 | img1, img2, img3
2 | 4,5,6 | test4,test5,test6 | img4, img5, img6
Edit #2:
I think this is the query you were looking for:
SELECT p.id, GROUP_CONCAT(l.user_id) As userList, GROUP_CONCAT(l.username) AS usernameList, GROUP_CONCAT(l.avatar) AS avatarList
FROM products AS p
LEFT JOIN (
SELECT l.product_id, l.user_id, u.username, u.avatar
FROM likes AS l
LEFT JOIN users AS u ON u.id = l.user_id
ORDER BY l.created_at DESC
LIMIT 10
)
AS l ON l.product_id = p.id
WHERE p.id IN (1,2,3)
GROUP BY p.id

MySQL order results by the sum of two relations count(*)

I would like to order the results by the count(*) of two related tables entries.
So I would like to get the top ordered user IDs that have more comments + posts interactions.
User Table:
ID Name ...
1 Jonh
2 Mark
3 King
4 Doe
Post Table:
ID USER_ID...
1 1
2 1
3 3
4 1
Comment Table:
ID USER_ID...
1 1
2 3
3 1
4 4
Ordered by POSTs count(*):
SELECT user.*, COUNT(post.user_id) AS count FROM user
LEFT JOIN post ON user.id = post.user_id
GROUP BY user.id
ORDER BY count DESC
Ordered by COMMENTs count(*)
SELECT user.*, COUNT(comment.user_id) AS count FROM user
LEFT JOIN comment ON user.id = comment.user_id
GROUP BY user.id
ORDER BY count DESC
Ordered by POSTs + COMMENTs count(*)
Expected Result:
user_id: 1 (four interactions), 3 (two interactions), 4 (one interaction)
SELECT u.*, COUNT(interact.user_id)
FROM user u
LEFT JOIN ( SELECT user_id FROM post
UNION ALL
SELECT user_id FROM comment
) interact
ON u.user_id = interact.user_id
GROUP BY u.user_id
If you want more detail can use conditional count
SELECT u.*,
COUNT(CASE WHEN source = 'p' then 1 END) as total_p,
COUNT(CASE WHEN source = 'c' then 1 END) as total_c,
COUNT(interact.user_id) as total_interact
FROM user u
LEFT JOIN ( SELECT user_id, 'p' as source FROM post
UNION ALL
SELECT user_id, 'c' as source FROM comment
) interact
ON u.user_id = interact.user_id
GROUP BY u.user_id

Order by desc in MySQL

I have the following query:
SELECT u.first_name, o.created_at
FROM user AS u
INNER JOIN order AS o ON o.user_id = u.id
GROUP BY u.id
The data structure looks like this:
user 1
order 1
order 2
order 3
user 2
order 1
user 3
order 1 order 2
user 4
order 1 order 2 order 3 order 4 order 5
Currently the query is returning data as follow:
user 1 > order 1
user 2 > order 1
user 3 > order 1
user 4 > order 1
but I would like to have all last items of user order, as example below:
user 1 > order 3
user 2 > order 1
user 3 > order 2
user 4 > order 5
Is there a way to get this nicely?
I need to retrieve last order of each user to generate the report.
Try this query :-
SELECT u.first_name, o.created_at
FROM user AS u
INNER JOIN order AS o ON o.user_id = u.id
Where o.created_at=(SELECT MAX(o2.created_at)
FROM order o2
WHERE o.user_id = o2.user_id);
GROUP BY u.id
this SELECT MAX(o2.created_at) use for get MAX created_at
SELECT u.first_name, max(o.created_at) as max_created_at
FROM user u
INNER JOIN order AS o ON o.user_id = u.id
GROUP BY u.id

MySQL select top rows with same condition values

I don't know how to title this problem. Correct me if you have better words.
I have two tables, Users and Posts.
Users:
id | username | password | ...
Posts:
id | author_id | title | content | ...
Now I want to list the "most active" users - the users who have written the most posts. And specifically, I want the top 10 result.
SELECT u.username, COUNT(p.id) AS count
FROM Posts p, Users u
WHERE u.id=p.author_id
GROUP BY p.author_id
ORDER BY count DESC
LIMIT 10;
I can get the expected result. However, the ranking may not be "fair" if some users have same number of posts.
E.g., I may get results like:
User 1 | 14
User 2 | 13
...
User 9 | 4
User 10 | 4
Here, there are actually several more users who have 4 posts.
So, the top 10 could be not exactly 10 results. How can I get a more "fair" result that contains extra rows of users who have 4 posts?
This is the right solution, I think: you need the subquery to know how much post has the 10th place in your top ten. Then, you use the outer query to extract the users with almost that postcount.
SELECT u.username, COUNT(p.id) AS count
FROM Posts p
JOIN Users u ON u.id = p.author_id
GROUP BY p.author_id
HAVING COUNT(p.id) >=
(
SELECT COUNT(p.id) AS count
FROM Posts p
JOIN Users u ON u.id = p.author_id
GROUP BY p.author_id
ORDER BY count DESC
LIMIT 9, 1
)
ORDER BY count DESC
Maybe not the best solution
select u.username, COUNT(p.id) AS count
FROM Posts p
join Users u on u.id = p.author_id
GROUP BY p.author_id
having COUNT(p.id) in
(
SELECT COUNT(p.id)
FROM Posts p
join Users u on u.id = p.author_id
GROUP BY p.author_id
ORDER BY count DESC
LIMIT 10
)
ORDER BY count DESC
Try this:
SELECT username, PostCount
FROM (SELECT username, PostCount, IF(#PostCount = #PostCount:=PostCount, #idx:=#idx+1, #Idx:=1) AS idx
FROM (SELECT u.username, COUNT(p.id) AS PostCount
FROM Posts p
INNER JOIN Users u ON u.id=p.author_id
GROUP BY p.author_id
) AS A, (SELECT #PostCount:=0, #Idx:=1) AS B
ORDER BY PostCount DESC
) AS A
WHERE idx <= 10;

Double count within a table

I have a table users:
user_id name
1 John
2 Dan
3 Jane
4 Sophie
5 Jodie
I then have a table named associates:
user_id assoc_id
1 2
1 3
3 4
3 1
3 5
4 1
5 1
5 2
What I want to do is show how many associates each user has, or none
So, the results would show
user_id Name Number of Associates
1 John 2
2 Dan 0
3 Jane 3
4 Sophie 1
5 Jodie 2
What I'm trying works but does not show those with 0
Here's what I'm trying, how do I get the 0s?
SELECT u.user_id, u.name, count(a.user_id) as howmany from users u
join associates a on a.user_id = u.user_id
group by u.user_id order by u.user_id asc
You need a LEFT OUTER JOIN:
SELECT u.user_id, u.name, count(a.user_id) as howmany
FROM users u LEFT OUTER JOIN associates a
ON a.user_id = u.user_id
GROUP BY u.user_id
ORDER BY u.user_id ASC
The LEFT OUTER JOIN will include every rows of the first table even is they aren't present in the joined table.
Try this :
SELECT u.user_id,
u.name,
count(a.user_id) as howmany
FROM users u
LEFT OUTER JOIN associates a
ON a.user_id = u.user_id
GROUP BY u.user_id
ORDER BY u.user_id ASC
Try using a left join instead of a inner join, that should select the rows that do not have corresponding associates.
SELECT u.user_id, u.name, count(a.user_id) as howmany
from users u
left join associates a
on a.user_id = u.user_id
group by u.user_id
order by u.user_id asc
This will work with strict rules also (if i had not made any typo):
SELECT users.*, ISNULL(associatesCount.AssocCount, 0) AS AssocCount
FROM users
LEFT JOIN (
SELECT user_id, COUNT(*) AS AssocCount
FROM associates
GROUP BY user_id
) AS associatesCount
ON users.user_id = associatesCount.user_id