Reputation: 533
I've been at it for a day and couldn't really figure it out. I'm making a page where users can post and other users can reply and like.
I have these three tables: posts, replies, and likes. FYI that they all have a different number of rows, see full details below.
Posts table
post_id message
------- -------
1 This is post #1
2 This is post #2
3 This is post #3
Replies table
reply_id post_id message
------- ------- -------
1 1 This is a reply to post #1
2 1 This is a reply to post #1
3 2 This is a reply to post #2
4 2 This is a reply to post #2
5 3 This is a reply to post #3
Likes table
like_id post_id liked
------- ------- -------
1 1 Yes
2 1 Yes
3 1 Yes
4 2 Yes
5 2 Yes
6 3 Yes
7 3 Yes
Those are the structure of my tables. What I need to achieve is like this below:
All tables joined and tallied
post_id total_replies total_likes
------- ------------- -----------
1 2 3
2 2 2
3 1 2
Basically, for the 1st post, it should show that it has 2 replies and 3 likes. I cannot seem to do it using two counts. It is giving me incorrect numbers.
select posts.post_id, count(replies.post_id) as total_replies, count(likes.post_id) as total_likes from posts
inner join replies on posts.post_id = replies.post_id
inner join likes on posts.post_id = likes.post_id
group by posts.post_id
Upvotes: 0
Views: 45
Reputation: 1624
I took your query and made a small edit so that it produces desired output. However note that the query would not be efficient. Gordon’s solution is more effective in terms of efficiency.
select
posts.post_id,
count(distinct replies.reply_id) as total_replies,
count(distinct likes.like_id) as total_likes
from posts
inner join replies on posts.post_id = replies.post_id
inner join likes on posts.post_id = likes.post_id
group by
posts.post_id;
Or would be better to pre-aggregate your metrics. That way it would be bit more efficient.
select
posts.post_id,
r.total_replies,
L.total_likes
from posts
inner join (Select post_id, count(reply_id) as total_replies from replies group by post_id) r on posts.post_id = r.post_id
inner join (Select post_id, count(like_id) as total_likes from likes group by posts_id) L on posts.post_id = L.post_id;
The caveat is you won’t get post that weren’t liked or replied upon since you are doing inner join. To get all posts irrespective of likes or replies you have to do left join.
Upvotes: 1
Reputation: 1269803
I would suggest correlated subqueries:
select p.post_id,
(select count(*)
from replies r
where r.post_id = p.post_id
) as total_replies,
(select count(*)
from likes l
where l.post_id = p.post_id
) as total_likes
from posts p;
The problem with your query is that you are joining along two different dimensions, so you are getting a Cartesian product -- all likes and all replies for a given post.
Not only does this get rid of that problem, but with indexes on replies(post_id)
and likes(post_id)
this should have better performance than any solution that does an aggregation over all the data.
Upvotes: 2