How to count make an SQL based on 3 tables - mysql

I have three tables and Im trying to count the number of likes per user on all his/her post.
USER TABLE
id name
1 John
2 Joe
POSTS TABLE
id user_id post_title
1 1 Some Title
2 1 Another Title
3 2 Yeah Title
LIKES TABLE
id post_id
1 1
2 1
3 1
4 2
5 3
My expected output is
ID LIKES
1 4
2 1
Im kinda stuck with the code below. I don't know how to add and count the likes table.
SELECT *
FROM user
INNER JOIN posts
ON user.id = posts.user_id;

You need to extend the join to the LIKES table and then use GROUP BY to group by the user ID and COUNT() all of the records for that user...
SELECT user.id, COUNT(likes.id)
FROM user
INNER JOIN posts ON user.id = posts.user_id
INNER JOIN likes ON posts.id = likes.post_id
GROUP BY user.id
If you want to list people who don't have posts or likes, then you should use outer joins (so change INNER JOIN to LEFT JOIN) so that these users show up.

For your desired result, you don't need the user table. You can simply do:
SELECT p.user_id, COUNT(*)
FROM posts p JOIN
likes l
ON l.post_id = p.id
GROUP BY p.user_id;
The only information you are taking from users is the id, which is already in posts. This assumes that all the user_id values in posts are valid, but that seems like a very reasonable assumption.

Related

Joining three tables with 2 count function

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
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.
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.

How to get a summary using only SQL from 3 tables

I have 3 tables:
Table posts
post_id
category_id
title
Table visits
visit_id
post_id
visit_date
Table categories
category_id
category_name
I need to get something like this (assuming that table visits has 10 rows)
Category 1 (categories.category_name) / visits(count) = 5
Category 2 (categories.category_name) / visits(count) = 1
Category 3 (categories.category_name) / visits(count) = 4
How can I get a similar result using only MySQL?
I tried with INNERs, no success
It's a simple query. You need to join all three tables, and finally group the result by category name. It should look like:
select
c.category_name,
count(*)
from categories c
join posts p on p.categoryid = c.category_id
join visits v on v.post_id = p.post_id
group by c.category_name
order by c.category_name

MySQL Frequency of frequency report

Given many users have many posts
I have a table of posts that has a foreign key user_id
I want to generate a report that shows the frequency of users against frequency of posts
e.g.
3 users wrote 2 posts each
2 users wrote 1 post each
1 user wrote 4 posts
Number of users | Number of posts
--------------- | ------------------
1 | 4
2 | 1
3 | 2
My attempt:
SELECT inner_table.frequency_posts,
Count(*) AS frequency_users
FROM posts
INNER JOIN (SELECT user_id,
Count(*) AS frequency_posts
FROM posts
GROUP BY user_id) AS inner_table
ON posts.user_id = inner_table.user_id
GROUP BY inner_table.frequency_posts
I think frequency_posts is working but counting frequency_users isn't giving the right values - when I look at the inner select on it's own and manually add up the posts I don't get the same values
You have to use Group by twice:
SELECT
COUNT(*) AS NumberOfUsers,
foo.NumberOfPosts
FROM
(SELECT
p.UserId AS UserId,
COUNT(*) AS NumberOfPosts
FROM
posts AS p
GROUP BY UserId) as foo
GROUP BY foo.NumberOfPosts

Joining two tables based upon the highest COUNT(*) from table2?

Okay, so I have two tables that I need to link together with a JOIN query. There is a table called likes and a table called users. The users table looks something like this
id name
----- ------
1 Mark
2 Mike
3 Paul
4 Dave
5 Chris
6 John
The likes table looks like this.
user_one user_two match_id
----- ------ --------
1 2 abc
2 1
1 3 acc
3 1 abb
1 5 aee
5 1
The expected result should be
id name
----- ------
1 Mark
The two tables should only be linked on the rows in the likes table where the users_one column is set to the value that is most commonly found in that column. In this case, the user with the id of 1 is in the likes table with the user_one column 3 times where the match_id isn't empty.
I've thought it out to be written something like this
SELECT users.*, likes.COUNT(*) AS count
FROM users
JOIN likes
ON users.id = likes.user_one
WHERE likes.match_id != ''
But, I know this isn't correct. Is there a way to link two tables with a JOIN only on the most common rows in one of the tables?
Would Grouping work for what you need... ?
SELECT users.id, users.name, count(*) AS count
FROM users
JOIN likes
ON users.id = likes.user_one
WHERE likes.match_id != ''
group by users.id, users.name
should give you something like
1 Mark 3
Should be something like this, if I understood the question
select top 1 user_one, name
from likes
inner join users ON users.id = likes.user_one
where match_id != ''
group by user_one
order by count(*) Desc
Are you looking for something like this?
select u.id, u.name, count(*)
from users u
inner join likes l
on l.id = l.user_one and l.match_id != ''
group by u.id, u.name
order by count(*) desc
limit 1
The limit 1, combined with sorting by the # of likes in descending order will result in getting one user - the one with the most matched likes.
Try:
select *
from users
where id in (
select id
from likes
group by id
order by count(*) desc, id
limit 1
)
The subquery returns the id of the row with the most appearances in the likes table (group by id and order by count(*) desc). I've added id to the order by to give predictable results in case there are multiple with the same number of appearances. This is used to join to the users table to give the resultset required.

SQL Join and show results if join false

I have a table - comments. Users can post if not a member of the site but want to show their details if they are.
So if a user comments who is NOT a member I show their posts but don't link to their profile, because they don't have one.
So, in the following query I want to return the rows even if there is no join:
select wc.comment, wc.comment_by_name, wc.user_id, u.url from comments wc
join users u on wc.wag_uid = u.user_id
where id = '1237' group by wc.comment order by wc.dateadded desc
I want to return:
comment comment_by_name user_id url
------- --------------- ------- ----
hello dan 12 /dan
hey jane /jane
world jack 10 /jack
But the above does not return the data for jane as she does not have a user_id
Is there a way to return all data even if the join is null?
use LEFT JOIN instead
SELECT wc.comment, wc.comment_by_name, wc.user_id, u.url
FROM comments wc
LEFT JOIN users u
on wc.wag_uid = u.user_id
WHERE id = '1237'
GROUP BY wc.comment
ORDER BY wc.dateadded DESC
basically INNER JOIN only select records which a record from one table has atleast one match on the other table while LEFT JOIN select all rows from the left hand side table (in your case, it's comments) whether it has no match on the other table.