MySQL ranking with GROUP BY and SUM - mysql

I have a point table with some columns being:
| user_id | points |
--------------------
| 1 | 10 |
| 5 | 10 |
| 5 | 50 |
| 3 | 15 |
| 3 | 10 |
I would like to get the rank for each user with MySQL.
I've seen this post MySQL get row position in ORDER BY but it doesn't have a SUM in it and I can't get it to work with SUM.
I would like to be able to get the rank of a given user_id.
Thanks in advance for your help!

Reminding the OP's question:
I would like to be able to get the rank of a given user_id.
In order to actually perform operations over the #rank you have to user another derived table (yes, it is inefficient, that's why it is better not to handle this in MySQL):
SELECT * FROM (
SELECT s.*, #rank := #rank + 1 rank FROM (
SELECT user_id, sum(points) TotalPoints FROM t
GROUP BY user_id
) s, (SELECT #rank := 0) init
ORDER BY TotalPoints DESC
) r
WHERE user_id = 3
Output
| USER_ID | TOTALPOINTS | RANK |
|---------|-------------|------|
| 3 | 25 | 2 |
The process is basically:
Get the total amounts of points per user
Sort by those total points ranking
Filter once you have the rank (otherwise, the rank will be compromised)
Fiddle here.

Try this::
SELECT #rownum:=#rownum + 1 as row_number,
t.*
FROM (
select user_id, SUM(points) as Addedpoint
from mytable group by user_id order by Addedpoint desc
) t,
(SELECT #rownum := 0) r

You can achieve that with subquery, inside which you should calculate your sum:
SELECT
#rank:=#rank+1 AS rank,
user_id,
total_points
FROM
(SELECT
user_id,
SUM(points) AS total_points
FROM t
GROUP BY
user_id) AS sum_points
CROSS JOIN
(SELECT #rank:=0) AS init
ORDER BY
sum_points.total_points DESC
-see my fiddle.

select
#rownum := #rownum + 1 AS position,
user_id,
total_points
from
(select user_id, sum(points) as total_points from table)a
join
(SELECT #rownum := 0) r
order by total_points desc

Using a user variable you can do something like this:-
SELECT user_id, tot_points, #Rank:=#Rank + 1 AS user_rank
FROM
(
SELECT user_id, SUM(points) AS tot_points
FROM SomeTable
GROUP BY user_id
ORDER BY tot_points DESC
) Sub1
CROSS JOIN (SELECT #Rank:=0) Sub2

http://sqlfiddle.com/#!2/d0be9/1
SET #rank=0;
select #rank:=#rank+1 AS rank, pointsScored.user_id, sumPoints
from (
select user_id , SUM(points)as sumPoints
from point
group by user_id
order by sumPoints desc
)as pointsScored

Related

User-defined variable in ranking statement

I will appreaciate any help on this issue. I already spent hours without any real solution.
I have a SQL
SELECT to_place, rank
FROM
(SELECT g1.to_place as to_place, g1.pcount as pcount,
#rank := IF(#current_to_place = g1.to_place, #rank + 1, 1) AS rank,
#current_to_place := g1.to_place
FROM
(select
to_place, count(*) as pcount
from temp_workflows
group by to_place
order by to_place,pcount desc) g1
ORDER BY g1.to_place, g1.pcount DESC) ranked
In table g1, I am grouping my data to find the most common occurrence of to_place.And then I want to rand those occurrences in ascending order (so I can later select top 3 of the most common occurrences per each to_place category.
The issue is that the user-defined variable is unpredictable (#rank is sometimes always 1) which probably is related to the fact that in one statement, I should not reference the same variable (current_to_place). I read a lot about using separate statements etc. but I could find a way to write my statement in a different way. How can I define #current_to_place elsewhere so the result is the same?
Thanks in advance for your help.
I think you should be testing pcount to get rank and you should initialise variables
DROP TABLE IF EXISTS T;
CREATE TABLE T
(to_place int);
insert into t values (1),(2),(2),(3),(3),(3);
SELECT to_place, rank
FROM
(
SELECT g1.to_place as to_place, g1.pcount as pcount,
#rank := IF(#current_to_place <> pcount, #rank + 1, 1) AS rank,
#current_to_place := pcount
FROM
(select
to_place, count(*) as pcount
from t
group by to_place
order by to_place,pcount desc) g1
cross join(select #rank:=0,#current_to_place:=0) r
ORDER BY g1.pcount DESC
)
ranked
+----------+------+
| to_place | rank |
+----------+------+
| 3 | 1 |
| 2 | 2 |
| 1 | 3 |
+----------+------+
3 rows in set (0.016 sec)

MySQL - Rank user amongst list of top high-scores

I have a MySQL query that selects the players with the top 3 highest scores in my users table, then creates an extra column where their rank is assigned:
SELECT s.*, #curRank := #curRank + 1 AS rank
FROM users AS s
JOIN
( SELECT DISTINCT highscore
FROM users
ORDER BY highscore DESC
LIMIT 3
) AS lim
ON s.highscore = lim.highscore
, (SELECT #curRank := 0) r
ORDER BY s.highscore DESC ;
Current progress
So, if the table looked like this:
userid name highscore
0 sam 20
1 james 39
2 jack 10
3 harry 46
4 jennie 7
The result of the query would be this:
userid name highscore rank
3 harry 46 1
1 james 39 2
0 sam 20 3
Question
How could I alter this query so it also display's the user and their rank in the list of results? E.g. if $userName = "jenny", how could I return:
userid name highscore rank
3 harry 46 1
1 james 39 2
0 sam 20 3
4 jenny 7 5 <-- Add this row and skip jack
Edit: I should mention - I am using MySQL version 5.0.96
For versions prior to 8.0...
SELECT *
FROM
( SELECT a.*
, #i := #i+1 i
FROM my_table a
JOIN (SELECT #i:=0) vars
ORDER
BY highscore DESC
, userid
) x
WHERE name = 'jennie'
OR i <= 3
ORDER
BY i;
https://www.db-fiddle.com/f/hYsiCE1bXeTnR2HzoJkKiR/0
If using MySQL 8+, then the RANK analytic function makes this easy:
WITH cte AS (
SELECT *, RANK() OVER (ORDER BY highscore DESC) rnk
FROM users
)
SELECT userid, name, highscore, rnk
FROM cte
WHERE rnk <= 3;
Try this,
select * from (
(select t1.userid,t1.name,t1.highscore,#rank1:=#rank1+1 'rank'
from (SELECT userid, name, highscore FROM users order by highscore desc) t1
join (select #rank1:=0) r
limit 3)
union
(select t2.userid,t2.name,t2.highscore,t2.rank from (select t1.userid,t1.name,t1.highscore,#rank2:=#rank2+1 'rank'
from (SELECT userid, name, highscore FROM users order by highscore desc) t1
join (select #rank2:=0) r) t2
where t2.name='jennie')) t;

reverse the serial number of a grouped query

The following query orders votes based on how many times users voted... I would like to know the # in the queue of the specific user.
SELECT #s:=#s+1 serial_number, user_id, COUNT(slug_owner) as cnt
FROM `votes_queue`,(SELECT #s:= 0) AS s
GROUP BY slug_owner
ORDER BY cnt DESC
serial_number | user_id | cnt 
3 | 19 | 8
2 | 14 | 4
1 | 13 | 2
Essentially i need the numbers in the serial_number column to be reversed so I can tell that user 13 is #3 based on votes ..
Assign the serial numbers after generating the ordered count:
SELECT #s:=#s+1 serial_number, temp.*
FROM (
SELECT user_id, COUNT(slug_owner) as cnt
FROM `votes_queue`,
GROUP BY slug_owner
ORDER BY cnt DESC
) temp, (SELECT #s:= 0) AS s
The serial numbers are not reversed because GROUP BY and variables don't mix. An ORDER BY is fine. You can use #hjpotter92's solution, but the following also fixes the problem:
SELECT (#rn := #rn + 1) as serial_number, t.*
FROM (SELECT serial_number, user_id, COUNT(slug_owner) as cnt
FROM `votes_queue`
GROUP BY slug_owner
) t CROSS JOIN
(SELECT #rn := 0) params
ORDER BY cnt DESC;
I think the performance is the same. I am offering this answer just to clarify what the actual problem is.

mysql Ranking system based on user id

I have a question about the query originally from http://jimmod.com/blog/2008/09/displaying-row-number-rownum-in-mysql/
SELECT IF(#points=p.points, #rownum, #rownum:=#rownum+1) rank,
p.*, (#points:=p.points) dummy
FROM `ranking` p, (SELECT #rownum:=0) x, (SELECT #points:=0) y
ORDER BY `points` DESC
Result of the query is:
rank user_id name score
-----------------------
1 4 Barney 100
2 2 Ted 88
3 1 Marshall 76
3 3 Robin 76
4 5 Lily 68
How can I get the same rank result based on user id?
Query below is based on user id, but the rank of Robin will be 4, not 3 like it should be.
SELECT rank FROM (SELECT #rownum:=#rownum+1 rank, p.*
FROM `ranking` p, (SELECT #rownum:=0) r
ORDER BY `points` DESC) a WHERE a.user_id = 3
Without testing it, seems like this should work using a subquery:
SELECT rank
FROM (
SELECT IF(#points=p.points, #rownum, #rownum:=#rownum+1) rank,
p.*, (#points:=p.points) dummy
FROM `ranking` p, (SELECT #rownum:=0) x, (SELECT #points:=0) y
ORDER BY `points` DESC
) t
WHERE user_id = 3
Your query above displays 4 because you're not using RANK, but rather just ROW NUMBER, and Robin shows up after Marshall (even though they have the same score).
Good luck.

How to find rows in SQL / MySQL with ORDER BY

I have a table user
Name | Poin
==================
user1 | 20
user2 | 30
user3 | 80
user4 | 60
user5 | 10
user6 | 85
And I have SQL query
SELECT *
FROM user
ORDER BY poin
It would appear that the data sequence based on points.
But what I need is data like this (for example, I was user1):
Position 1 : user6 - 85 point
Position 2 : user3 - 80 point
Position 3 : user4 - 60 point
You are position 5 : user1 - 20 point
UPDATE
I use this sql
SELECT x.name,
x.position
FROM (SELECT t.user,
#rownum := #rownum + 1 AS position
FROM user t
JOIN (SELECT #rownum := 0) r
ORDER BY t.poin DESC) x
WHERE x.user = 'user1'
This will give current rank for user1:
SELECT count(*) AS rank
FROM user
WHERE poin >= (SELECT poin FROM user WHERE name = 'user1')
Small issue with this query is that if another user has the same points, it will be assigned the same rank - whether it is correct, it is questionable.
If you want to simply add rank for every user, use this:
SELECT
#rank:=#rank+1 AS rank,
name,
poin
FROM user,
(SELECT #rank:=0) r
ORDER BY poin DESC
You can use small variation of this query to get rank of single user, but avoid issue of the same ranking ambiguity:
SELECT *
FROM (
SELECT
#rank:=#rank+1 AS rank,
name,
poin
FROM user,
(SELECT #rank:=0) r
ORDER BY poin DESC
) x
WHERE name = 'user1'
select * from user order by poin desc
Hope this helps
SELECT * FROM user ORDER BY poin DESC
The ORDER BY keyword is used to sort the result-set by a specified column.
The ORDER BY keyword sorts the records in ascending order by default.
If you want to sort the records in a descending order, you can use the DESC keyword.
SQL ORDER BY Syntax
SELECT column_name(s)
FROM table_name
ORDER BY column_name(s) ASC|DESC
SELECT Name,
Poin,
#rowNum := #rowNum + 1 AS position
FROM user
JOIN (SELECT #rowNum := 0) r
ORDER BY poin;
The following code:
SELECT count(*) AS rank
FROM user
WHERE poin >= (SELECT poin FROM user WHERE name = 'user1')
Has a problem when is has double point in different rows.
SELECT Name,
Poin,
#rowNum := #rowNum + 1 AS position
FROM user
JOIN (SELECT #rowNum := 0) r
ORDER BY poin DESC;