Select most popular users given by likes, posts and comments - mysql

I have a forum and I would like to find the most popular users. The most popular users definded by the most likes on posts and comments and also defined by the most total posts and comments. A user with the most likes (order 1), most posts (order 2) and most comments (order 3) is the most popular. Same logic applies for the next (second) most popular user.
So I have 3 tables:
posts table
id user_id likes
1 1 0
2 1 0
3 1 0
4 1 0
5 1 0
6 1 1
7 1 0
8 2 0
9 2 2
10 2 0
11 2 0
12 3 0
13 3 0
14 4 0
15 4 10
comments table
id user_id likes
1 1 0
2 1 1
3 1 1
4 1 0
5 2 0
6 2 2
7 2 1
8 4 1
9 4 0
users table
id name
1 John
2 Adam
3 Maggie
4 Steve
The likes column contains the likes given by other users on the respective post (row).
I tried:
SELECT DISTINCT c.id, c.name,
SUM(a.likes), SUM(b.likes), (SUM(a.likes) + SUM(b.likes)) as popular,
COUNT(a.id) as mostp, COUNT(b.id) as mostc
FROM posts as a, comments as b, users as c
WHERE a.user_id=b.user_id AND a.user_id=c.id AND b.user_id=c.id
GROUP BY a.user_id, b.user_id ORDER BY popular DESC, mostp DESC, mostc DESC
Obviously, this does not work, because if you test the query it gives more likes(sum) than expected.
Here is the live query
http://sqlfiddle.com/#!2/08900/3

The problem with your query is with users that have more than one post and more than one comment, resulting in a cartesian product and producing the wrong sums.
The query below (example on SQL Fiddle) should work, since the subqueries already group by user_id:
SELECT
u.name,
COALESCE(p.likes,0) + COALESCE(c.likes,0) AS likes,
COALESCE(p.cnt,0) AS post_count,
COALESCE(c.cnt,0) AS comment_count
FROM users u
LEFT JOIN (
SELECT user_id, COUNT(1) AS cnt, SUM(likes) AS likes
FROM posts
GROUP BY user_id
) p ON ( p.user_id = u.id )
LEFT JOIN (
SELECT user_id, COUNT(1) AS cnt, SUM(likes) AS likes
FROM comments
GROUP BY user_id
) c ON ( c.user_id = u.id )
ORDER BY likes DESC, post_count DESC, comment_count DESC;
Result:
| NAME | LIKES | POST_COUNT | COMMENT_COUNT |
-----------------------------------------------
| Steve | 11 | 2 | 2 |
| Adam | 5 | 4 | 3 |
| John | 3 | 7 | 4 |
| Maggie | 0 | 2 | 0 |

Here is how you can do this
SELECT
u.id,
u.name,
(l.likes + r.likes) As TotalLikes,
IFNULL(posts,0) AS TotalPosts,
IFNULL(comments,0) AS TotalComments
FROM users AS u
LEFT JOIN (SELECT
user_id,
IFNULL(SUM(likes),0) as likes,
COUNT(likes) as posts
FROM posts
GROUP BY user_id) AS l
on l.user_id = u.id
LEFT JOIN (SELECT
user_id,
IFNULL(SUM(likes),0) as likes,
COUNT(likes) AS comments
FROM comments
GROUP BY user_id) AS r
on r.user_id = u.id
ORDER BY TotalLikes DESc
SQL Fiddle Demo
Output
| ID | NAME | TOTALLIKES | TOTALPOSTS | TOTALCOMMENTS |
---------------------------------------------------------
| 4 | Steve | 11 | 2 | 2 |
| 2 | Adam | 5 | 4 | 3 |
| 1 | John | 3 | 7 | 4 |
| 3 | Maggie | 0 | 2 | 0 |

Related

IN MYSQL how to pick up the largest count each row?

In mysql. I want to get some the largest counted review_id and mention.
below table is raw data
>> table Named: topic_id_review_id
id topic_id review_id
+-------+-------------+-------------+
1 1 1
2 1 1
3 1 1
4 2 1
5 4 1
6 3 2
7 5 2
8 2 3
9 2 3
10 3 3
11 3 1
12 5 4
13 5 1
14 6 5
16 7 7
17 7 7
18 7 7
...
....
**I want to get the top of four(4) topics about each of review_id.
>> wanted!!
topic_id review_id
+-------------+-------------+
1 1
2 1
3 1
4 1
3 2
5 2
2 3
3 3
5 4
...
....
like above table, I want to top 4 topics in Every review_id.
I have already tried some code. but all of thing is not good results...
SELECT
tr1.topic_id, tr1.review_id
FROM topic_id_review_id tr1
WHERE review_id IN (1,2,3)AND(
SELECT COUNT(*)FROM topic_id_review_id tr2
WHERE tr2.review_id = tr1.review_id) < 4;
SELECT
COUNT(*) AS `count`
FROM topic_id_review_id tr2
WHERE tr2.review_id = 1
GROUP BY topic_id, review_id
order by `count` DESC LIMIT 4;
maybe your guy, this not difficult... but as beginner to me...
it is not simple. so please let me know to to solve it! thanks
One way that works in all versions of MySql is the use of a correlated subquery in the WHERE clause which counts the number of topic_ids for each review_id:
select
t.topic_id, t.review_id
from (
select min(id) id, topic_id, review_id
from topic_id_review_id
group by topic_id, review_id
) t
where (
select count(*) from topic_id_review_id
where review_id = t.review_id and topic_id <= t.topic_id and id < t.id
) <= 4
order by t.review_id, t.topic_id
See the demo.
For MySql 8.0+:
with cte as (
select
t.topic_id, t.review_id,
row_number() over (partition by t.review_id order by t.topic_id, t.id) rn
from (
select min(id) id, topic_id, review_id
from topic_id_review_id
group by topic_id, review_id
) t
)
select
c.topic_id, c.review_id
from cte c
where c.rn <= 4
order by c.review_id, c.topic_id
See the demo.
Results:
| topic_id | review_id |
| -------- | --------- |
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
| 4 | 1 |
| 3 | 2 |
| 5 | 2 |
| 2 | 3 |
| 3 | 3 |
| 5 | 4 |
| 6 | 5 |
| 7 | 7 |

mysql get user rank using inner join

I have three tables as following. And i want to get user with highest rank.
1) users table as
id | user_id | created_at
1 | 100 | 2014-11-07 02:54:09
2 | 102 | 2014-11-08 03:52:40
3 | 103 | 2014-11-10 02:47:26
4 | 104 | 2014-11-11 02:54:48
5 | 105 | 2014-11-14 03:11:23
6 | 105 | 2014-11-15 00:56:34
2) user_profile table as
id | user_id | rank
1 | 100 | 100
2 | 102 | 500
3 | 103 | 10
4 | 104 | 0
5 | 105 | 11
6 | 105 | 1000
3) user_followers table as
id | user_id | followers
1 | 100 | 10
2 | 102 | 20
3 | 103 | 30
4 | 104 | 40
5 | 105 | 0
6 | 105 | 50
Now my query is i want to get list of users short by highest rank in table2. In case of tie user with the highest followers in table3 will win. In case of same followers user who is created first will win.
And another one i want to find user rank with same logic passing by user id.
I already tried something like
SET #i=0;
SELECT user_id, rank, #i:=#i+1 AS rank FROM user_profile ORDER BY rank DESC
Arion's answer looked like this...
SELECT
users.*
FROM
users
JOIN user_profile
ON users.user_id = user_profile.user_id
JOIN user_followers
ON user_profile.user_id=user_followers.user_id
ORDER BY
user_profile.rank DESC,
user_followers.followers DESC,
users.created_at DESC
...but this seems a little closer to what you're after...
SELECT u.user_id
, u.created_at
, up.rank
, uf.followers
, #i:=#i+1 corrected_rank
FROM users u
LEFT
JOIN user_profile up
ON up.user_id = u.user_id
LEFT
JOIN user_followers uf
ON uf.user_id = u.user_id
CROSS
JOIN (SELECT #i:=1) v
ORDER
BY rank DESC
, followers DESC
, created_at ASC;
SET #rank = 0;
SELECT
#rank := #rank + 1 AS rank, *
FROM
(
SELECT users.user_id, user_profile.rank, user_followers.followers, users.created_at
FROM users
LEFT JOIN user_profile ON users.user_id = user_profile.user_id
LEFT JOIN user_followers ON users.user_id = user_followers.user_id
ORDER BY user_profile.rank DESC, user_followers.followers DESC, users.created_at ASC
)

mySql INNER JOIN, MAX & DISTINCT

I'm looking to return one row for each user of type "student" displaying their "name" and their latest "score" (in reverse chronological order).
I have two tables users & services
users Table
id name type
---|-------|-----
1 | Bob | student
2 | Dave | student
3 | Larry | student
4 | Kevin | master
services table
id score userId date
---|--------|-------|------------
1 | 14 | 1 | 2014-09-04
2 | 99 | 3 | 2014-09-03
3 | 53 | 2 | 2014-09-07
4 | 21 | 1 | 2014-09-08
5 | 79 | 2 | 2014-09-08
6 | 43 | 3 | 2014-09-10
7 | 72 | 3 | 2014-09-10
8 | 66 | 2 | 2014-09-01
9 | 43 | 3 | 2014-08-22
10 | 26 | 1 | 2014-08-22
Desired Result
id scores name date
---|--------|-------|------------
3 | 43 | Larry | 2014-09-10
1 | 21 | Bob | 2014-09-08
2 | 79 | Dave | 2014-09-08
What I have tried is:
SELECT users.id, users.name, services.date, services.score
FROM users
JOIN services ON users.id = services.userId
WHERE users.type='student'
ORDER BY services.date DESC
But this always returns the last date in the table for each user.
So i decided to try and tackle it from the other end like this:
SELECT servicesTemp.date, servicesTemp.score
FROM services servicesTemp
INNER JOIN
(SELECT userId, MAX(date) AS MaxExpDate
FROM services
GROUP BY clientId) servicesTempGrp
ON servicesTemp.userId = servicesTempGrp.userId
AND servicesTemp.MaxDate = servicesTempGrp.MaxDate
But realised that i would end up with duplicates if the dates were ever the same and i can only return one row per user (and double grouping didn't work).
I think i'm now over complicating this, so a life line would be much appreciated.
try:
SELECT users.id, users.name, services.date, services.score
FROM users
JOIN services ON users.id = services.userId
WHERE users.type='client'
AND services.date = (SELECT MAX(date) from services where userID = users.id)
ORDER BY services.date DESC
You can guarantee one row by using the substring_index()/group_concat() trick:
SELECT u.id, u.name, max(s.date) as date,
substring_index(group_concat(s.score order by date desc), ',', 1) as score
FROM users u JOIN
services s
ON u.id = s.userId
WHERE u.type = 'client'
GROUP BY u.id, u.name
ORDER BY s.date DESC;
Without using group by, another option for getting only one row per user is to use variables. Or, if you know the ids are being assigned sequentially, use the id instead of date:
SELECT u.id, u.name, s.date, s.score
FROM users u INNER JOIN
services s
on u.userId = s.userId INNER JOIN
(SELECT userId, MAX(id) AS MaxId
FROM services
GROUP BY userId
) smax
ON s.userId = smax.userId and s.Id = smax.MaxId
WHERE u.type = 'client';

Frequency join statement

I have 2 tables:
users:
id | name
-----------
1 | user 1
2 | user 2
3 | user 3
and posts:
id | userId | text
--------------------
1 | 1 | text 1
2 | 1 | text 2
3 | 2 | text 3
4 | 2 | text 4
5 | 2 | text 5
6 | 2 | text 6
I need to retrieve users ordered by post-frequency, e.g.:
id | name | posts
-------------------
2 | user 2 | 4
1 | user 1 | 1
3 | user 3 | 0
Some users might not have posts!
Currently I have 2 queries and doing it in 3 steps:
retrieve all users
retrieve all posts grouped by userId
use php to join the above
Question
Is the above possible to do in a single sql query?
select u.id, u.name, count(p.id) as posts
from users u
left join posts p on p.userid = u.id
group by u.id, u.name
order by posts desc
Another version, which prevents listing all fields from users table in group by clause. Also more fast in many cases IMO.
select u.id, u.name, coalesce(c.cnt, 0) as posts
from users u
left join
(select userId, couint(*) cnt from posts group by userId) c
on u.id = c.userId

mysql select related items based on current item

i have a scenario, lets say 3 users review a business of id 10, how can i get all the unique id of the user who review that business and and use that unique id to find another business review which is not equal to 10 ?
sample table user_review:
review_id | user_id | business_id | rating | review_date
1 2 10 3 20121030124001
2 2 9 3 20121022120627
3 2 10 4 20121023120627
4 3 10 4 20121024120627
5 3 6 3 20121022140627
6 4 10 2 20121025120627
7 4 10 5 20121030120627
8 3 10 2 20121010120627
9 4 8 5 20121028120627
i should get result of these
review_id | user_id | business_id | rating | review_date
2 2 9 3 20121022120627
5 3 6 3 20121022140627
9 4 8 5 20121028120627
In a above result if there is 2 reviews for a same user_id and same business_id the latest one should be return.Thanks
Try this query:
Here is link to sqlfidle with running results http://sqlfiddle.com/#!2/cd4ea/1
SELECT
tblreview.*,tblusers.user_name
FROM
(
SELECT
MAX(tblreview.review_id) review_id
, tblreview.user_id
FROM
(
SELECT
DISTINCT user_id
FROM
tblreview
WHERE business_id = 10
) reviewsb10
INNER JOIN
tblreview
ON
tblreview.user_id = reviewsb10.user_id
AND
tblreview.business_id <> 10
GROUP BY
user_id
) tblLastReviewPerUser
INNER JOIN
tblreview
ON
tblreview.review_id = tblLastReviewPerUser.review_id
INNER JOIN
tblusers
ON
tblusers.user_id = tblLastReviewPerUser.user_id
SELECT t1.*
FROM TableName t1
INNER JOIN
(
SELECT user_id, business_id, MAX(review_date) review_date
FROM TableName
WHERE business_id <> 10
GROUP BY user_id, business_id
) t2 ON t1.user_id = t2.user_id
AND t1.review_date = t2.review_date
AND t1.business_id = t2.business_id
Try this query -
SELECT * FROM (
SELECT * FROM user_review
ORDER BY IF(business_id = 10, 1, 0), review_date DESC
) t
GROUP BY user_id
HAVING
COUNT(IF(business_id = 10, 1, NULL)) > 0
AND COUNT(IF(business_id <> 10, 1, NULL)) > 0
+-----------+---------+-------------+--------+----------------+
| review_id | user_id | business_id | rating | review_date |
+-----------+---------+-------------+--------+----------------+
| 2 | 2 | 9 | 3 | 20121022120627 |
| 5 | 3 | 6 | 3 | 20121022140627 |
| 9 | 4 | 8 | 5 | 20121028120627 |
+-----------+---------+-------------+--------+----------------+