mysql get user rank using inner join - mysql

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
)

Related

MySQL How to count multiple entries while maintaining unique fileds

I have 3 tables that look like this:
acc_prop
id | pid | uid
1 | 10 | 1
2 | 11 | 1
3 | 12 | 1
cal
id | pid
1 | 10
2 | 11
3 | 12
price
cid | rate
1 | 100
2 | 99
3 | 130
I want to create a query that returns a pid, a count of uid's with the same uid, and the rate for that pid.
expected result
pid | uid_count | rate
10 | 3 | 100
11 | 3 | 99
12 | 3 | 130
my query looks like this
SELECT
cal.pid,
count(ap3.uid) as uid_count,
price.rate
FROM
price
JOIN
cal on cal.id = price.cid
JOIN
acc_prop ap using(pid)
JOIN
acc_prop ap2 on ap2.uid = ap.uid
JOIN
acc_prop ap3 on ap3.uid = ap2.uid
group by ap3.pid;
But it returns
the incorrect count
the incorrect pid list
the incorrect rate
actual result
pid | uid_count | rate
10 | 9 | 100
10 | 9 | 100
I think what you are after is this, viz to pre-calculate the number of users in acc_prop as a derived table, which you can then join through to the rest of the query:
SELECT
cal.pid,
UserCount.Cnt,
price.rate
FROM
price
JOIN cal on cal.id = price.cid
JOIN acc_prop ap using(pid)
JOIN
(
SELECT uid, COUNT(*) AS Cnt
FROM acc_prop
GROUP BY uid
) UserCount
ON ap.uid = UserCount.uid;
SqlFiddle here
I think cal is not needed here, do you tried this:
SELECT
acc_prop.pid, (SELECT COUNT(*) FROM acc_prop WHERE uid = uid) AS uid_count, price.rate
FROM
acc_prop
INNER JOIN price
ON acc_prop.id = price.cid

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';

Select monthly counts of data across two tables in mysql

I have two tables, lastfm_scrobbles and lastfm_annotations. Example data:
mysql> select * from lastfm_scrobbles limit 5;
+---------+---------+-----------+---------------------+
| user_id | item_id | artist_id | scrobble_time |
+---------+---------+-----------+---------------------+
| 1469 | 45651 | 1 | 2010-06-30 13:57:42 |
| 1469 | 45651 | 1 | 2011-03-28 15:43:37 |
| 6872 | 45653 | 1 | 2013-08-03 15:07:44 |
| 7044 | 1370 | 1 | 2007-03-26 17:07:26 |
| 7044 | 1370 | 1 | 2007-08-24 18:41:35 |
+---------+---------+-----------+---------------------+
mysql> select * from lastfm_annotations limit 5;
+---------+---------+-----------+--------+------------+
| user_id | item_id | artist_id | tag_id | tag_month |
+---------+---------+-----------+--------+------------+
| 121 | 1330412 | 1330412 | 475 | 2006-12-01 |
| 121 | 1330412 | 1330412 | 517 | 2006-12-01 |
| 121 | 1330412 | 1330412 | 7280 | 2006-12-01 |
| 121 | 1330412 | 1330412 | 21384 | 2006-12-01 |
| 121 | 1330412 | 1330412 | 27872 | 2006-12-01 |
+---------+---------+-----------+--------+------------+
Furthermore, I have a user information table (lastfm_users). The details of this aren't important, but what is relevant is that the query:
select user_id from lastfm_users where scrobbles_recorded==1;
Returns the users I care about for the purposes of this question.
Ok, with that preamble out of the way: I need a query that will get me, for those users, the total number of entries they have in both the scrobbles and annotations tables for each month. In other words, the result should look something like:
user_id y m scrobble_count anno_count
123 2006 3 100 50
456 2008 11 321 10
... and so on
Make sense? I believe the query I want is a combination of the following:
select year(tag_month) as y, month(tag_month) as m, count(*) as anno_count
from lastfm_annotations where user_id in (select user_id from
lastfm_users where scrobbles_recorded=1)
group by user_id, year(tag_month), month(tag_month);
select year(scrobble_time) as y, month(scrobble_time) as m, count(*) as scrobble_count
from lastfm_scrobbles where user_id in (select user_id from
lastfm_users where scrobbles_recorded=1)
group by user_id, year(scrobble_time), month(scrobble_time);
But I'm not certain of the correct way to generate the join query to get the result I want. Suggestions?
You can try
select user_id, y, m,
coalesce(sum(case when source = 1 then total end), 0) anno_count,
coalesce(sum(case when source = 2 then total end), 0) scrobble_count
from
(
select 1 source, a.user_id, year(tag_month) y, month(tag_month) m, count(*) total
from lastfm_annotations a join lastfm_users u
on a.user_id = u.user_id
where u.scrobbles_recorded = 1
group by user_id, year(tag_month), month(tag_month)
union all
select 2 source, s.user_id, year(scrobble_time), month(scrobble_time), count(*)
from lastfm_scrobbles s join lastfm_users u
on s.user_id = u.user_id
where u.scrobbles_recorded = 1
group by user_id, year(scrobble_time), month(scrobble_time)
) q
group by user_id, y, m
or just
select user_id, y, m,
sum(case when source = 1 then 1 else 0 end) anno_count,
sum(case when source = 2 then 1 else 0 end) scrobble_count
from
(
select 1 source, a.user_id, year(tag_month) y, month(tag_month) m
from lastfm_annotations a join lastfm_users u
on a.user_id = u.user_id
where u.scrobbles_recorded = 1
union all
select 2 source, s.user_id, year(scrobble_time), month(scrobble_time)
from lastfm_scrobbles s join lastfm_users u
on s.user_id = u.user_id
where u.scrobbles_recorded = 1
) q
group by user_id, y, m;
Here is SQLFiddle demo

Select most popular users given by likes, posts and comments

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 |

MySQL left join 2 tables order by count

I have 2 tables as below and want to have select both of them result by count(column) but doesn't work please advise.
review table
ID | RID | Name | comment
555|3000 | John | John comment
555|3001 | Ben | Ben comment
555|3002 | Smith| Smith comment
Likes table
U | PID
1 | 3000
2 | 3000
3 | 3000
4 | 3001
Expected result
ID | RID | Name | comment | votes
555|3000 | John | John comment | 3
555|3001 | Ben | Ben comment | 1
I'm expecting the result from select * from review with count PID column from Likes table
My current query is
SELECT * , (SELECT COUNT( PID ) FROM Likes AS votes WHERE there.ID = PID)
FROM review AS there
LEFT JOIN Likes b ON there.RID = b.PID
WHERE ID =555
AND there.RID = b.PID AND votes>0
ORDER BY votes DESC
But it did not woking, please advise.
Since you are after on reviews with votes only, you can make your query shorter(and perhaps faster) by converting LEFT JOIN to INNER JOIN, and eliminating detection of COUNT: http://www.sqlfiddle.com/#!2/1f920/3
SELECT r.ID, r.RID, r.Name, `Comment`, COUNT(RID) as votes
FROM review r
JOIN Likes l ON l.PID = r.RID
WHERE r.ID = 555
GROUP BY r.RID
ORDER BY votes DESC
Output:
| ID | RID | NAME | COMMENT | VOTES |
--------------------------------------------
| 555 | 3000 | John | John comment | 3 |
| 555 | 3001 | Ben | Ben comment | 1 |
SELECT ID, RID, Name, `Comment`, COUNT(RID) as votes
FROM review AS there
LEFT JOIN Likes b ON there.RID = b.PID
WHERE ID = 555
AND there.RID = b.PID
GROUP BY b.PID
HAVING votes > 0
ORDER BY votes DESC
sqlfiddle