The database structure is as follows:
users
[id] [name] [etc...]
1 Echolot ...
2 AnotherUser ...
posts
[id] [user_id] [title] [etc...]
1 1 Post1 ...
2 1 Post2 ...
posts_upvotes
[id] [post_id] [user_id (user who voted)] [value (1 or 0 or -1)]
1 1 1 1
2 1 2 -1
3 2 1 1
4 2 2 1
I'm trying to get a list of all users and their total amount of upvotes sum(value)gathered by all their posts like so:
id: 1
name: Echolot
upvoteCount: 2
id: 2
name: AnotherUser
upvoteCount: 0
I have tried using the following query but without success, I get wrong values and not the right amount of results:
SELECT joinedUsers.id, joinedPosts.postCount, joinedPostsUpvotes.upvoteCount
FROM users joinedUsers
inner join (
SELECT user_id, id, count(id) as postCount
FROM posts
GROUP BY user_id, id
) joinedPosts on joinedPosts.user_id = joinedUsers.id
inner join (
SELECT post_id, sum(value) as upvoteCount
FROM posts_upvotes
GROUP BY post_id
) joinedPostsUpvotes on joinedPostsUpvotes.post_id = joinedPosts.id
GROUP BY joinedUsers.id, joinedPosts.postCount, joinedPostsUpvotes.upvoteCount
Thank you in advance for any hints or solutions.
EDIT: Added test data and expected results.
You don't need all those subqueries, you can just use a couple left join between user, post and post_upvotes:
SELECT t1.id, t1.name, count(distinct t2.id), sum(t3.value)
FROM users t1
LEFT JOIN
posts t2
ON t1.id = t2.user_id
LEFT JOIN
posts_upvotes t3
ON t2.id = t3.post_id
GROUP BY t1.id, t1.name
The use of left join instead of inner join ensures you that users with no posts or posts with no votes will be displayed as well.
I think this would do the trick for you:
SELECT u.id,
u.name,
COUNT(p.id)
SUM(pu.value)
FROM users AS u
JOIN posts AS p
ON u.id = p.user_id
JOIN posts_upvotes AS pu
ON p.id = pu.post_id
GROUP BY u.id,
u.name
If I am not missing something in your question.
I think it would be something like this if you want the count of the amount of times someone voted (not tested)
SELECT users.id, users.name, COUNT(posts_upvotes.id) as upvoteCount
FROM users
INNER JOIN posts_upvotes ON posts_upvotes.user_id = users.id
GROUP BY users.id
Or this if you want the sum of the value of the upvotes:
SELECT users.id, users.name, SUM(posts_upvotes.id) as upvoteSum
FROM users
INNER JOIN posts_upvotes ON posts_upvotes.user_id = users.id
GROUP BY users.id
Or in Laravel you could use the Query Builder: https://laravel.com/docs/5.4/queries
Something like this:
DB::table('users')
->join('posts_upvotes', 'users.id', '=', 'posts_upvotes.user_id'))
->groupBy('users.id')
->select('users.id', 'users.name', DB::raw('COUNT(posts_upvotes) as upvoteCount'))
->get();
Related
I have 3 tables like so
Table 1: UserInfo
user_id userName
123 userOne
Table 2: Post
user_id postContent
123 This is test message
Table 3: LikePost
user_id likesPostId
123 This is test message
I would like to run a query to get total number of post likes, posts, and user information from those 3 tables.
I can do this for each one such as in Post table:
SELECT COUNT(*) FROM Post WHERE Post.user_id = '123'
and SELECT * FROM UserInfo WHERE UserInfo.user_id = '123'
Is anyone have better solution in just 1 query? Thank you so much!
Use a structured query (with subqueries) something like this.
SELECT u.user_id, u.userName, p.num postcount, l.num likecount
FROM UserInfo u
LEFT JOIN (
SELECT COUNT(*) num,
user_id
FROM Post
GROUP BY user_id
) p ON u.user_id = p.user_id
LEFT JOIN (
SELECT COUNT(*) num,
user_id
FROM LikePost
GROUP BY user_id
) l ON u.user_id = l.user_id
What's going on here? The two subqueries, for example
SELECT COUNT(*) num,
user_id
FROM LikePost
GROUP BY user_id
each generate a virtual table with either zero or one row per user_id, showing a count for each user_id. You then join those virtual tables to your UserInfo table.
Use LEFT JOIN because ordinary innner JOIN will suppress users that lack either posts or likes.
Try This
SELECT ui.userName,Count(p.*),
Count(lp.*) as TotalPostLikes
FROM UserInfo ui
INNER JOIN Post p on p.user_id=ui.user_id
INNER JOIN LikePost lp on lp.user_id=ui.user_id
WHERE ui.user_id = '123'
GROUP BY ui.userName
If you want to select Username, Post and Likes on post, try the following
SELECT ui.userName,p.postContent as PostContent,
(SELECT COUNT(lp.user_id) FROM LikePost lp
WHERE lp.user_id=ui.user_id) as Likes,
(SELECT COUNT(_p .user_id) FROM Post _p
WHERE _p .user_id=ui.user_id) as TotalPosts
FROM UserInfo ui
INNER JOIN Post p on p.user_id=ui.user_id
WHERE ui.user_id = '123'
Yes you can do it within one query using leftjoin on Post and LikePost like below
SELECT COUNT(*),User.userName FROM UserInfo as User
leftjoin Post as Post on Post.user_id = User.user_id
leftjoin LikePost as LikePost on LikePost.user_id = User.user_id
where Post.user_id = 123
group by Post.user_id
I am struggling with writing a query join in mysql
I have two table
Challenges
challenge_ID(int) |to_user(int)|from_user(int)|timestamp|gameID=>nullable
Users
iduser(int)|first_name(string)
I want get the first name of to_user and form_user when I have the challengeID
for instance if
Challenges
challenge_ID(int) |to_user(int)|from_user(int)|timestamp|gameID
1 9 10 sometimestamp
Users
iduser(int)|first_name(string)
9 Tom
10 Chris
11 Patrick
I would like to get 'Tom' and 'Chris' for challenge id 1
Thanks for your help.
It may be something like this:
SELECT first_name
FROM Users
WHERE iduser IN (SELECT to_user
FROM challenges
WHERE Challenge_Id = 1
UNION
SELECT from_user
FROM challenges
WHERE Challenge_Id = 1)
this is how you do this
select c.* ,u.first_name as to_name , u2.first_name as from_name
FROM challenges c
join users u on c.to_user = u.id
join users u2 on c.from_user = u2.id
where c.challenge_ID = 1
It looks like you need a UNION and then a JOIN:
SELECT users.first_name
FROM (
SELECT from_user AS usr FROM challenges WHERE challenge_id=1
UNION
SELECT to_user FROM challenges WHERE challenge_id=1
) u INNER JOIN users ON u.usr = users.id_user
UNION will remove duplicates on the subquery, if there are no duplicates you can use UNION ALL which is faster.
You can try this query
SELECT u_to.first_name, u_from.first_name
FROM challenges c
INNER JOIN users u_to ON u_to.iduser = c.to_user
INNER JOIN users u_from ON u_from.iduser = c.from_user
WHERE c.challange_ID = 1
I have three tables, users, activities and purchases.
Users has many activities and purchases, activities has 4 types.
I need to query users like this:
[
{
user_id: 1,
// from activities
post_count: 2,
updated_count: 3,
print_count: 4,
share_count: 5,
// from purchases
purchase_count: 6
},
...
]
I use this sql:
SELECT u.id, post.post_count, updated.update_count, print.print_count, share.share_count, purchase.purchase_count
FROM users as u
LEFT JOIN (
SELECT user_id, activity_type, count(*) as post_count
FROM activities
WHERE activity_type = 1
GROUP BY user_id
) post
ON u.id = post.user_id
LEFT JOIN (
SELECT user_id, activity_type, count(*) as update_count
FROM activities
WHERE activity_type = 2
GROUP BY user_id
) updated
ON u.id = updated.user_id
LEFT JOIN (
SELECT user_id, activity_type, count(*) as print_count
FROM activities
WHERE activity_type = 3
GROUP BY user_id
) print
ON u.id = print.user_id
LEFT JOIN (
SELECT user_id, activity_type, count(*) as share_count
FROM activities
WHERE activity_type = 4
GROUP BY user_id
) share
ON u.id = share.user_id
LEFT JOIN (
SELECT user_id, count(*) AS purchase_count
FROM purchases
GROUP BY user_id
) purchase
ON u.id = purchase.user_id
how can i optimize performance with this query?
Great thanks to Eugen Rieck
I modified his query to this, then it works.
SELECT
users.id AS user_id,
SUM(IF((activities.activity_type=1),1,0)) AS post_count,
SUM(IF((activities.activity_type=2),1,0)) AS update_count,
SUM(IF((activities.activity_type=3),1,0)) AS print_count,
SUM(IF((activities.activity_type=4),1,0)) AS share_count,
IFNULL(purchase.count,0) AS purchase_count
FROM
users
LEFT JOIN activities ON activities.user_id=users.id
LEFT JOIN (
SELECT user_id, count(*) AS count
FROM purchases
GROUP BY user_id
) purchase
ON users.id = purchase.user_id
GROUP BY users.id
Currently you run the activities table 4 times - this could be folded into one:
SELECT
users.id AS user_id,
SUM(IF(activites.activity_type=1,1,0)) AS post_count,
SUM(IF(activites.activity_type=2,1,0)) AS update_count,
SUM(IF(activites.activity_type=3,1,0)) AS print_count,
SUM(IF(activites.activity_type=4,1,0)) AS share_count,
IFNULL(COUNT(purchases.id),0) AS purchase_count
FROM
users
INNER JOIN activities ON activities.user_id=users.id
LEFT JOIN purchases ON purchases.user_id=users.id
GROUP BY users.id
Okay I tried to look all over stackoverflow, and the closest solution I found is this:
mysql AND clause on same column multiple times
But I can't use statements and "having" syntax won't work because of group by. There MUST be a simple solution to this.
The 2 tables looks like this (simplified):
users:
uid name
1 person 1
2 person 2
3 person 3
categories:
uid value
1 actor
1 musician
2 actor
3 dancer
4 musician
4 dancer
I want to get the uid of those that are 2 values at the same time. For example, I want to get the UID that is an actor AND a musician. Not just one value, but both of them must be required!
First I tried this:
SELECT users.uid, users.name
FROM
users
LEFT OUTER JOIN categories ON users.uid = categories.uid
WHERE (categories.value = 'actor' AND categories.value = 'musician')
GROUP BY u.uid;
This of course does not work since one row can't have 2 values.
Does anyone know a solution?
You can JOIN to the categories table multiple times to get the result:
SELECT users.uid, users.name
FROM users
INNER JOIN categories c1
ON users.uid = c1.uid
INNER JOIN categories c2
ON users.uid = c2.uid
WHERE c1.value = 'actor'
AND c2.value = 'musician';
See SQL Fiddle with Demo
SELECT users.uid, users.name
FROM users
LEFT JOIN categories ON users.uid = categories.uid
WHERE categories.value in ('actor', 'musician')
GROUP BY u.uid, users.name
having count(distinct categories.value) = 2;
Use a having clause
SELECT u.uid, u.name
FROM users u
LEFT OUTER JOIN categories c ON u.uid = c.uid
WHERE c.value = 'actor' OR c.value = 'musician'
GROUP BY u.uid
having count(distinct c.value) > 1
If you really do not want to use having you could try this:
SELECT uid, name
FROM users
WHERE
uid IN (SELECT uid FROM categories WHERE value='actor')
AND uid IN (SELECT uid FROM categories WHERE value='musician')
But there is really nothing wrong with using HAVING ;)
I have social application let's say like twitter, where user can follow other users and can likes some comments.
What I need to fetch is followers of a user and ids of last 2 comments liked by each follower(if any), also the count of follower's followers, using MySQL.
Here are the tables
Table user_follower
User_id follower_id
1 2
2 3
1 5
1 6
1 7
Table user_likes
comment_id User_id date
41 2 some_date
42 2 some_date
41 5 some_date
42 5 some_date
43 5 some_date
43 2 some_date
43 6 some_date
how can we do this in a single mysql query?
so far i am able to get the followers and count of follower's follower and following both.
select uf.follower_id,
(select count(*) from user_followers uf1 where uf1.follower_id = uf.follower_id) as following_count,
(select count(*) from user_followers uf2 where uf2.user_id = uf.follower_id) as follower_count,
from user_followers uf
join users u on u.id = uf.follower_id
where uf.user_id = 1
what i want is now to get the 2 latest comment_ids for each follower, i.e. uf.follower_id here.
if not possible in the same query,
i am fine even with another query as will passing the follower_ids as in parameter, but it should give me 2 latest comment for each passed id..
I think this will work. I have not tested it so it may have some syntax errors. Given the level of nesting used in this query I suspect it will perform badly with a very large dataset.
SELECT follower_id, num_followers, GROUP_CONCAT(comment_id)
FROM (
SELECT t.*,
#r := IF(#g = t.follower_id, #r+1, 1) RowNum,
#g := t.follower_id
FROM (select #g:=null) AS initvars
INNER JOIN (
SELECT followers.*, ul.comment_id
FROM (
SELECT
uf1.user_id,
uf1.follower_id,
COUNT(uf2.follower_id) AS num_followers
FROM user_follower uf1
LEFT JOIN user_follower uf2
ON uf1.follower_id = uf2.user_id
WHERE uf1.user_id = 1
GROUP BY uf1.user_id, uf1.follower_id
) AS followers
LEFT JOIN user_likes ul
ON followers.follower_id = ul.user_id
ORDER BY followers.follower_id ASC, comment_id DESC
) AS t
) AS final
WHERE RowNum < 3
GROUP BY follower_id, num_followers;
UPDATE Here is the other query using the inequality join -
SELECT tmp.follower_id, COUNT(uf2.follower_id) AS num_followers, tmp.comments
FROM (
SELECT follower_id, GROUP_CONCAT(comment_id ORDER BY comment_id DESC) AS comments
FROM (
SELECT uf.follower_id, ul1.*
FROM user_follower uf
LEFT JOIN user_likes ul1
ON uf.follower_id = ul1.user_id
LEFT JOIN user_likes ul2
ON uf.follower_id = ul2.user_id
AND ul1.comment_id < ul2.comment_id
WHERE uf.user_id = 1
GROUP BY ul1.user_id, ul1.comment_id
HAVING COUNT(ul2.comment_id) < 2
) AS tmp
GROUP BY tmp.follower_id
) AS tmp
LEFT JOIN user_follower uf2
ON tmp.follower_id = uf2.user_id
GROUP BY tmp.follower_id